Skip to content

Commit 07fe487

Browse files
Caching: HybridCache-only (net9.0, Hybrid 9.3.0) with minimal options and tests
1 parent 03e7fae commit 07fe487

13 files changed

+222
-237
lines changed

Polly.slnx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<File Path="eng/analyzers/Test.globalconfig" />
3838
</Folder>
3939
<Folder Name="/src/">
40+
<Project Path="src/Polly.Caching/Polly.Caching.csproj" />
4041
<Project Path="src/Polly.Core/Polly.Core.csproj" />
4142
<Project Path="src/Polly.Extensions/Polly.Extensions.csproj" />
4243
<Project Path="src/Polly.RateLimiting/Polly.RateLimiting.csproj" />
@@ -46,6 +47,7 @@
4647
</Folder>
4748
<Folder Name="/test/">
4849
<Project Path="test/Polly.AotTest/Polly.AotTest.csproj" />
50+
<Project Path="test/Polly.Caching.Tests/Polly.Caching.Tests.csproj" />
4951
<Project Path="test/Polly.Core.Tests/Polly.Core.Tests.csproj" />
5052
<Project Path="test/Polly.Extensions.Tests/Polly.Extensions.Tests.csproj" />
5153
<Project Path="test/Polly.RateLimiting.Tests/Polly.RateLimiting.Tests.csproj" />

src/Polly.Caching/CacheResiliencePipelineBuilderExtensions.cs

Lines changed: 0 additions & 54 deletions
This file was deleted.

src/Polly.Caching/CacheResilienceStrategy.cs

Lines changed: 0 additions & 66 deletions
This file was deleted.

src/Polly.Caching/CacheStrategyOptions.cs

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using Polly.Caching;
3+
4+
namespace Polly;
5+
6+
/// <summary>
7+
/// Extensions for integrating HybridCache with <see cref="ResiliencePipelineBuilder"/>.
8+
/// </summary>
9+
public static class HybridCacheResiliencePipelineBuilderExtensions
10+
{
11+
/// <summary>
12+
/// Adds a HybridCache-based caching strategy to an untyped resilience pipeline.
13+
/// </summary>
14+
/// <param name="builder">The pipeline builder.</param>
15+
/// <param name="options">The HybridCache strategy options.</param>
16+
/// <returns>The same builder instance.</returns>
17+
[UnconditionalSuppressMessage(
18+
"Trimming",
19+
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
20+
Justification = "Options are validated and all members preserved.")]
21+
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(HybridCacheStrategyOptions))]
22+
public static ResiliencePipelineBuilder AddHybridCache(this ResiliencePipelineBuilder builder, HybridCacheStrategyOptions options)
23+
{
24+
Guard.NotNull(builder);
25+
Guard.NotNull(options);
26+
27+
return builder.AddStrategy(
28+
_ => new HybridCacheResilienceStrategy<object>(options),
29+
options);
30+
}
31+
32+
/// <summary>
33+
/// Adds a HybridCache-based caching strategy to a typed resilience pipeline producing TResult.
34+
/// </summary>
35+
/// <typeparam name="TResult">The result type of the pipeline.</typeparam>
36+
/// <param name="builder">The typed pipeline builder.</param>
37+
/// <param name="options">The HybridCache strategy options.</param>
38+
/// <returns>The same typed builder instance.</returns>
39+
[UnconditionalSuppressMessage(
40+
"Trimming",
41+
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
42+
Justification = "Options are validated and all members preserved.")]
43+
public static ResiliencePipelineBuilder<TResult> AddHybridCache<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TResult>(
44+
this ResiliencePipelineBuilder<TResult> builder,
45+
HybridCacheStrategyOptions<TResult> options)
46+
{
47+
Guard.NotNull(builder);
48+
Guard.NotNull(options);
49+
50+
return builder.AddStrategy(
51+
_ => new HybridCacheResilienceStrategy<TResult>(options),
52+
options);
53+
}
54+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using Microsoft.Extensions.Caching.Hybrid;
2+
using Polly.Utils;
3+
4+
namespace Polly.Caching;
5+
6+
internal sealed class HybridCacheResilienceStrategy<TResult> : ResilienceStrategy<TResult>
7+
{
8+
private readonly HybridCache _cache;
9+
private readonly Func<ResilienceContext, string?> _keyGen;
10+
11+
public HybridCacheResilienceStrategy(HybridCacheStrategyOptions<TResult> options)
12+
{
13+
Guard.NotNull(options);
14+
if (options.Cache is null)
15+
{
16+
throw new ArgumentException("Cache must not be null.", nameof(options));
17+
}
18+
19+
_cache = options.Cache;
20+
_keyGen = options.CacheKeyGenerator ?? (ctx => ctx.OperationKey);
21+
}
22+
23+
protected override async ValueTask<Outcome<TResult>> ExecuteCore<TState>(
24+
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
25+
ResilienceContext context,
26+
TState state)
27+
{
28+
var key = _keyGen(context);
29+
if (string.IsNullOrEmpty(key))
30+
{
31+
return await callback(context, state).ConfigureAwait(context.ContinueOnCapturedContext);
32+
}
33+
34+
var result = await _cache.GetOrCreateAsync(
35+
key!,
36+
async ct =>
37+
{
38+
var outcome = await callback(context, state).ConfigureAwait(context.ContinueOnCapturedContext);
39+
outcome.ThrowIfException();
40+
return outcome.Result!;
41+
},
42+
cancellationToken: context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext);
43+
44+
return Outcome.FromResult(result);
45+
}
46+
}

src/Polly.Caching/CacheStrategyOptions.TResult.cs renamed to src/Polly.Caching/HybridCacheStrategyOptions.TResult.cs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,25 @@
1-
using System.ComponentModel.DataAnnotations;
1+
using System.ComponentModel.DataAnnotations;
22
using System.Diagnostics.CodeAnalysis;
3-
using Microsoft.Extensions.Caching.Memory;
3+
using Microsoft.Extensions.Caching.Hybrid;
44

55
namespace Polly.Caching;
66

77
/// <summary>
8-
/// Represents options for the caching strategy.
8+
/// Options for the HybridCache-based caching strategy.
99
/// </summary>
1010
/// <typeparam name="TResult">The result type.</typeparam>
11-
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Addressed with DynamicDependency on ValidationHelper.Validate method")]
12-
public class CacheStrategyOptions<TResult> : ResilienceStrategyOptions
11+
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Members preserved via builder validation.")]
12+
public class HybridCacheStrategyOptions<TResult> : ResilienceStrategyOptions
1313
{
14-
/// <summary>
15-
/// Gets or sets the memory cache instance to use.
16-
/// </summary>
14+
/// <summary>Gets or sets the HybridCache instance to use.</summary>
1715
[Required]
18-
public IMemoryCache? Cache { get; set; }
16+
public HybridCache? Cache { get; set; }
1917

20-
/// <summary>
21-
/// Gets or sets the time-to-live for cached entries.
22-
/// </summary>
18+
/// <summary>Gets or sets the time-to-live for cached entries.</summary>
2319
[Range(typeof(TimeSpan), "00:00:00", "365.00:00:00")]
2420
public TimeSpan Ttl { get; set; } = TimeSpan.FromMinutes(5);
2521

26-
/// <summary>
27-
/// Gets or sets a value indicating whether sliding expiration should be used.
28-
/// </summary>
22+
/// <summary>Gets or sets a value indicating whether sliding expiration should be used.</summary>
2923
public bool UseSlidingExpiration { get; set; }
3024

3125
/// <summary>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Polly.Caching;
2+
3+
/// <inheritdoc/>
4+
public class HybridCacheStrategyOptions : HybridCacheStrategyOptions<object>
5+
{
6+
}
Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,18 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>net8.0;net6.0;netstandard2.0;net472;net462</TargetFrameworks>
3+
<TargetFrameworks>net9.0</TargetFrameworks>
44
<AssemblyTitle>Polly.Caching</AssemblyTitle>
5-
<RootNamespace>Polly</RootNamespace>
5+
<RootNamespace>Polly.Caching</RootNamespace>
66
<Nullable>enable</Nullable>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<ProjectType>Library</ProjectType>
9-
<UsePublicApiAnalyzers>false</UsePublicApiAnalyzers>
9+
<UsePublicApiAnalyzers>true</UsePublicApiAnalyzers>
10+
<!-- TODO: Enable after first NuGet release with a published baseline -->
1011
<EnablePackageValidation>false</EnablePackageValidation>
1112
<LegacySupport>true</LegacySupport>
13+
<MutationScore>100</MutationScore>
1214
</PropertyGroup>
1315

14-
<PropertyGroup>
15-
<Description>Polly.Caching provides caching strategies for Polly v8 built on Microsoft.Extensions.Caching.*.</Description>
16-
<PackageTags>Polly Caching IMemoryCache IDistributedCache Resilience</PackageTags>
17-
</PropertyGroup>
18-
19-
<ItemGroup>
20-
<Using Include="Polly.Utils" />
21-
</ItemGroup>
22-
23-
<ItemGroup>
24-
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" VersionOverride="8.0.0" Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'netcoreapp3.1'))" />
25-
<PackageReference Include="Microsoft.Bcl.TimeProvider" Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'net8.0'))" />
26-
<PackageReference Include="System.Threading.Tasks.Extensions" Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'netcoreapp3.1'))" />
27-
<PackageReference Include="System.ValueTuple" Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETFramework'" />
28-
<PackageReference Include="System.ComponentModel.Annotations" VersionOverride="5.0.0" Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'netcoreapp3.1'))" />
29-
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
30-
</ItemGroup>
31-
3216
<ItemGroup>
3317
<Using Include="Polly.Utils" />
3418
<Compile Include="..\Polly.Core\Utils\ExceptionUtilities.cs" Link="utils\ExceptionUtilities.cs" />
@@ -37,7 +21,10 @@
3721
</ItemGroup>
3822

3923
<ItemGroup>
40-
<ProjectReference Include="..\Polly.Core\Polly.Core.csproj" />
24+
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" VersionOverride="9.3.0" />
4125
</ItemGroup>
4226

27+
<ItemGroup>
28+
<ProjectReference Include="..\Polly.Core\Polly.Core.csproj" />
29+
</ItemGroup>
4330
</Project>
Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
11
#nullable enable
2-
Polly.CacheResiliencePipelineBuilderExtensions
3-
Polly.Caching.CacheStrategyOptions
4-
Polly.Caching.CacheStrategyOptions<TResult>
2+
Polly.HybridCacheResiliencePipelineBuilderExtensions
3+
Polly.Caching.HybridCacheStrategyOptions
4+
Polly.Caching.HybridCacheStrategyOptions<TResult>
5+
Polly.Caching.HybridCacheStrategyOptions.HybridCacheStrategyOptions() -> void
6+
Polly.Caching.HybridCacheStrategyOptions<TResult>.HybridCacheStrategyOptions() -> void
7+
Polly.Caching.HybridCacheStrategyOptions<TResult>.Cache.get -> Microsoft.Extensions.Caching.Hybrid.HybridCache?
8+
Polly.Caching.HybridCacheStrategyOptions<TResult>.Cache.set -> void
9+
Polly.Caching.HybridCacheStrategyOptions<TResult>.Ttl.get -> System.TimeSpan
10+
Polly.Caching.HybridCacheStrategyOptions<TResult>.Ttl.set -> void
11+
Polly.Caching.HybridCacheStrategyOptions<TResult>.UseSlidingExpiration.get -> bool
12+
Polly.Caching.HybridCacheStrategyOptions<TResult>.UseSlidingExpiration.set -> void
13+
Polly.Caching.HybridCacheStrategyOptions<TResult>.CacheKeyGenerator.get -> System.Func<Polly.ResilienceContext!, string?>?
14+
Polly.Caching.HybridCacheStrategyOptions<TResult>.CacheKeyGenerator.set -> void
15+
static Polly.HybridCacheResiliencePipelineBuilderExtensions.AddHybridCache(this Polly.ResiliencePipelineBuilder! builder, Polly.Caching.HybridCacheStrategyOptions! options) -> Polly.ResiliencePipelineBuilder!
16+
static Polly.HybridCacheResiliencePipelineBuilderExtensions.AddHybridCache<TResult>(this Polly.ResiliencePipelineBuilder<TResult>! builder, Polly.Caching.HybridCacheStrategyOptions<TResult>! options) -> Polly.ResiliencePipelineBuilder<TResult>!

0 commit comments

Comments
 (0)