Skip to content

Commit 7a16bc7

Browse files
Address review: AOT ref, central HybridCache version, build.cake tasks, docs, static factory, non-empty key, tests updated
1 parent 07fe487 commit 7a16bc7

9 files changed

+65
-29
lines changed

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
1212
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" />
1313
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="4.14.0" />
14+
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.3.0" />
1415
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
1516
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.8" />
1617
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />

build.cake

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ Task("__CreateNuGetPackages")
183183
System.IO.Path.Combine(srcDir, "Polly.RateLimiting", "Polly.RateLimiting.csproj"),
184184
System.IO.Path.Combine(srcDir, "Polly.Extensions", "Polly.Extensions.csproj"),
185185
System.IO.Path.Combine(srcDir, "Polly.Testing", "Polly.Testing.csproj"),
186+
System.IO.Path.Combine(srcDir, "Polly.Caching", "Polly.Caching.csproj"),
186187
];
187188

188189
Information("Building NuGet packages");
@@ -268,12 +269,22 @@ Task("MutationTestsLegacy")
268269
RunMutationTests(File("./src/Polly/Polly.csproj"), File("./test/Polly.Specs/Polly.Specs.csproj"));
269270
});
270271

272+
Task("MutationTestsCaching")
273+
.IsDependentOn("__Setup")
274+
.Does((context) =>
275+
{
276+
RunMutationTests(
277+
File("./src/Polly.Caching/Polly.Caching.csproj"),
278+
File("./test/Polly.Caching.Tests/Polly.Caching.Tests.csproj"));
279+
});
280+
271281
Task("MutationTests")
272282
.IsDependentOn("MutationTestsCore")
273283
.IsDependentOn("MutationTestsRateLimiting")
274284
.IsDependentOn("MutationTestsExtensions")
275285
.IsDependentOn("MutationTestsTesting")
276-
.IsDependentOn("MutationTestsLegacy");
286+
.IsDependentOn("MutationTestsLegacy")
287+
.IsDependentOn("MutationTestsCaching");
277288

278289
///////////////////////////////////////////////////////////////////////////////
279290
// EXECUTION

src/Polly.Caching/HybridCacheResilienceStrategy.cs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Polly.Caching;
66
internal sealed class HybridCacheResilienceStrategy<TResult> : ResilienceStrategy<TResult>
77
{
88
private readonly HybridCache _cache;
9-
private readonly Func<ResilienceContext, string?> _keyGen;
9+
private readonly Func<ResilienceContext, string?> _keyGenerator;
1010

1111
public HybridCacheResilienceStrategy(HybridCacheStrategyOptions<TResult> options)
1212
{
@@ -17,30 +17,53 @@ public HybridCacheResilienceStrategy(HybridCacheStrategyOptions<TResult> options
1717
}
1818

1919
_cache = options.Cache;
20-
_keyGen = options.CacheKeyGenerator ?? (ctx => ctx.OperationKey);
20+
_keyGenerator = options.CacheKeyGenerator ?? (static ctx => ctx.OperationKey);
2121
}
2222

2323
protected override async ValueTask<Outcome<TResult>> ExecuteCore<TState>(
2424
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
2525
ResilienceContext context,
2626
TState state)
2727
{
28-
var key = _keyGen(context);
28+
var key = _keyGenerator(context);
2929
if (string.IsNullOrEmpty(key))
3030
{
31-
return await callback(context, state).ConfigureAwait(context.ContinueOnCapturedContext);
31+
return Outcome.FromException<TResult>(new InvalidOperationException("HybridCache key was null or empty."));
3232
}
3333

34+
var payload = new FactoryState<TState>(callback, context, state, context.ContinueOnCapturedContext);
35+
3436
var result = await _cache.GetOrCreateAsync(
35-
key!,
36-
async ct =>
37+
key,
38+
payload,
39+
static async (s, _) =>
3740
{
38-
var outcome = await callback(context, state).ConfigureAwait(context.ContinueOnCapturedContext);
41+
var outcome = await s.Callback(s.Context, s.State).ConfigureAwait(s.ContinueOnCapturedContext);
3942
outcome.ThrowIfException();
4043
return outcome.Result!;
4144
},
4245
cancellationToken: context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext);
4346

4447
return Outcome.FromResult(result);
4548
}
49+
50+
private readonly struct FactoryState<T>
51+
{
52+
public FactoryState(
53+
Func<ResilienceContext, T, ValueTask<Outcome<TResult>>> callback,
54+
ResilienceContext context,
55+
T state,
56+
bool continueOnCapturedContext)
57+
{
58+
Callback = callback;
59+
Context = context;
60+
State = state;
61+
ContinueOnCapturedContext = continueOnCapturedContext;
62+
}
63+
64+
public Func<ResilienceContext, T, ValueTask<Outcome<TResult>>> Callback { get; }
65+
public ResilienceContext Context { get; }
66+
public T State { get; }
67+
public bool ContinueOnCapturedContext { get; }
68+
}
4669
}

src/Polly.Caching/HybridCacheStrategyOptions.TResult.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,25 @@ namespace Polly.Caching;
1111
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Members preserved via builder validation.")]
1212
public class HybridCacheStrategyOptions<TResult> : ResilienceStrategyOptions
1313
{
14-
/// <summary>Gets or sets the HybridCache instance to use.</summary>
14+
/// <summary>Gets or sets the <see cref="HybridCache"/> instance to use.</summary>
1515
[Required]
1616
public HybridCache? Cache { get; set; }
1717

18-
/// <summary>Gets or sets the time-to-live for cached entries.</summary>
18+
/// <summary>
19+
/// Gets or sets the time-to-live for cached entries.
20+
/// The default is 5 minutes.
21+
/// </summary>
1922
[Range(typeof(TimeSpan), "00:00:00", "365.00:00:00")]
2023
public TimeSpan Ttl { get; set; } = TimeSpan.FromMinutes(5);
2124

22-
/// <summary>Gets or sets a value indicating whether sliding expiration should be used.</summary>
25+
/// <summary>
26+
/// Gets or sets a value indicating whether sliding expiration should be used.
27+
/// The default is <see langword="false"/>.
28+
/// </summary>
2329
public bool UseSlidingExpiration { get; set; }
2430

2531
/// <summary>
26-
/// Gets or sets a function that generates the cache key from the resilience context.
32+
/// Gets or sets a delegate that generates the cache key from the resilience context.
2733
/// If <see langword="null"/>, <see cref="ResilienceContext.OperationKey"/> is used.
2834
/// </summary>
2935
public Func<ResilienceContext, string?>? CacheKeyGenerator { get; set; }
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
namespace Polly.Caching;
22

33
/// <inheritdoc/>
4-
public class HybridCacheStrategyOptions : HybridCacheStrategyOptions<object>
5-
{
6-
}
4+
public class HybridCacheStrategyOptions : HybridCacheStrategyOptions<object>;

src/Polly.Caching/Polly.Caching.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>net9.0</TargetFrameworks>
3+
<TargetFramework>net9.0</TargetFramework>
44
<AssemblyTitle>Polly.Caching</AssemblyTitle>
55
<RootNamespace>Polly.Caching</RootNamespace>
66
<Nullable>enable</Nullable>
@@ -21,7 +21,7 @@
2121
</ItemGroup>
2222

2323
<ItemGroup>
24-
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" VersionOverride="9.3.0" />
24+
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" />
2525
</ItemGroup>
2626

2727
<ItemGroup>

test/Polly.AotTest/Polly.AotTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<TargetFramework>net9.0</TargetFramework>
99
</PropertyGroup>
1010
<ItemGroup>
11+
<ProjectReference Include="..\..\src\Polly.Caching\Polly.Caching.csproj" />
1112
<ProjectReference Include="..\..\src\Polly.Core\Polly.Core.csproj" />
1213
<ProjectReference Include="..\..\src\Polly.Extensions\Polly.Extensions.csproj" />
1314
<ProjectReference Include="..\..\src\Polly.RateLimiting\Polly.RateLimiting.csproj" />

test/Polly.Caching.Tests/HybridCacheResilienceStrategyTests.cs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public async Task Miss_Caches_Then_Hit()
4141
}
4242

4343
[Fact]
44-
public async Task EmptyKey_Bypasses_Cache()
44+
public async Task EmptyKey_Throws()
4545
{
4646
var services = new ServiceCollection();
4747
services.AddHybridCache();
@@ -60,15 +60,11 @@ public async Task EmptyKey_Bypasses_Cache()
6060
.AddHybridCache(options)
6161
.Build();
6262

63-
var r1 = await pipeline.ExecuteAsync(
64-
(Func<CancellationToken, ValueTask<string>>)(static _ => new("x")),
65-
CancellationToken.None);
66-
67-
var r2 = await pipeline.ExecuteAsync(
68-
(Func<CancellationToken, ValueTask<string>>)(static _ => new("y")),
69-
CancellationToken.None);
70-
71-
r1.ShouldBe("x");
72-
r2.ShouldBe("y");
63+
await Should.ThrowAsync<InvalidOperationException>(async () =>
64+
{
65+
_ = await pipeline.ExecuteAsync(
66+
(Func<CancellationToken, ValueTask<string>>)(static _ => new("x")),
67+
CancellationToken.None);
68+
});
7369
}
7470
}

test/Polly.Caching.Tests/Polly.Caching.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
</ItemGroup>
1919

2020
<ItemGroup>
21-
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" VersionOverride="9.3.0" />
21+
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" />
2222
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
2323
</ItemGroup>
2424

0 commit comments

Comments
 (0)