diff --git a/BlazorOutputCaching.ServiceInterface/MyServices.cs b/BlazorOutputCaching.ServiceInterface/MyServices.cs index 930a9f3..732f6a1 100644 --- a/BlazorOutputCaching.ServiceInterface/MyServices.cs +++ b/BlazorOutputCaching.ServiceInterface/MyServices.cs @@ -1,8 +1,10 @@ using ServiceStack; using BlazorOutputCaching.ServiceModel; +using Microsoft.AspNetCore.OutputCaching; namespace BlazorOutputCaching.ServiceInterface; +[OutputCache(Duration = 60)] public class MyServices : Service { public object Any(Hello request) diff --git a/BlazorOutputCaching.ServiceModel/BlazorOutputCaching.ServiceModel.csproj b/BlazorOutputCaching.ServiceModel/BlazorOutputCaching.ServiceModel.csproj index f51a96b..a1fd078 100644 --- a/BlazorOutputCaching.ServiceModel/BlazorOutputCaching.ServiceModel.csproj +++ b/BlazorOutputCaching.ServiceModel/BlazorOutputCaching.ServiceModel.csproj @@ -14,4 +14,10 @@ + + + ..\..\..\..\.dotnet\packs\Microsoft.AspNetCore.App.Ref\8.0.2\ref\net8.0\Microsoft.AspNetCore.OutputCaching.dll + + + diff --git a/BlazorOutputCaching/BlazorOutputCaching.csproj b/BlazorOutputCaching/BlazorOutputCaching.csproj index abaea4d..5c80017 100644 --- a/BlazorOutputCaching/BlazorOutputCaching.csproj +++ b/BlazorOutputCaching/BlazorOutputCaching.csproj @@ -47,6 +47,10 @@ + + + + diff --git a/BlazorOutputCaching/Configure.AppHost.cs b/BlazorOutputCaching/Configure.AppHost.cs index 58e9a2b..283ec2d 100644 --- a/BlazorOutputCaching/Configure.AppHost.cs +++ b/BlazorOutputCaching/Configure.AppHost.cs @@ -22,47 +22,4 @@ public override void Configure() } } -public class RedisOutputCacheStore : IOutputCacheStore -{ - private readonly IRedisClientsManager _redisManager; - - public RedisOutputCacheStore(IRedisClientsManager redisManager) - { - _redisManager = redisManager; - } - - public async ValueTask GetAsync(string key, CancellationToken cancellationToken) - { - await using var redis = await _redisManager.GetClientAsync(token: cancellationToken); - var value = await redis.GetAsync(key, cancellationToken); - return value; - } - - public async ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan validFor, CancellationToken cancellationToken) - { - await using var redis = await _redisManager.GetClientAsync(token: cancellationToken); - - // First persist in normal cache hashset - await redis.SetAsync(key, value, validFor, cancellationToken); - - if (tags == null) - return; - foreach (var tag in tags) - { - await redis.AddItemToSetAsync($"tag:{tag}", key, cancellationToken); - } - } - public async ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken) - { - await using var redis = await _redisManager.GetClientAsync(token: cancellationToken); - - var keys = await redis.GetAllItemsFromListAsync($"tag:{tag}", cancellationToken); - - foreach (var key in keys) - { - await redis.RemoveEntryAsync(key); - await redis.RemoveItemFromSetAsync($"tag:{tag}", key, cancellationToken); - } - } -} diff --git a/BlazorOutputCaching/Configure.OutputCache.cs b/BlazorOutputCaching/Configure.OutputCache.cs new file mode 100644 index 0000000..dfb7848 --- /dev/null +++ b/BlazorOutputCaching/Configure.OutputCache.cs @@ -0,0 +1,57 @@ +using Microsoft.AspNetCore.OutputCaching; +using ServiceStack.Redis; + +[assembly: HostingStartup(typeof(BlazorOutputCaching.ConfigureOutputCache))] + +namespace BlazorOutputCaching; + +public class ConfigureOutputCache : IHostingStartup +{ + public void Configure(IWebHostBuilder builder) + { + builder.ConfigureServices(services => + { + services.AddSingleton(c => + new BasicRedisClientManager("localhost:6379")); + services.AddSingleton(); + }); + } +} + +public class RedisOutputCacheStore(IRedisClientsManager redisManager) : IOutputCacheStore +{ + public async ValueTask GetAsync(string key, CancellationToken cancellationToken) + { + await using var redis = await redisManager.GetClientAsync(token: cancellationToken); + var value = await redis.GetAsync(key, cancellationToken); + return value; + } + + public async ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan validFor, CancellationToken cancellationToken) + { + await using var redis = await redisManager.GetClientAsync(token: cancellationToken); + + // First persist in normal cache hashset + await redis.SetAsync(key, value, validFor, cancellationToken); + + if (tags == null) + return; + foreach (var tag in tags) + { + await redis.AddItemToSetAsync($"tag:{tag}", key, cancellationToken); + } + } + + public async ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken) + { + await using var redis = await redisManager.GetClientAsync(token: cancellationToken); + + var keys = await redis.GetAllItemsFromListAsync($"tag:{tag}", cancellationToken); + + foreach (var key in keys) + { + await redis.RemoveEntryAsync(key); + await redis.RemoveItemFromSetAsync($"tag:{tag}", key, cancellationToken); + } + } +} \ No newline at end of file diff --git a/BlazorOutputCaching/Program.cs b/BlazorOutputCaching/Program.cs index c7786b9..d95f68c 100644 --- a/BlazorOutputCaching/Program.cs +++ b/BlazorOutputCaching/Program.cs @@ -17,8 +17,8 @@ var services = builder.Services; var config = builder.Configuration; -services.AddSingleton(new BasicRedisClientManager()); -services.AddSingleton(); +//services.AddSingleton(new BasicRedisClientManager()); +//services.AddSingleton(); services.AddOutputCache(); @@ -94,12 +94,26 @@ app.UseServiceStack(new AppHost(), options => { options.MapEndpoints(); - options.RouteHandlerBuilders.Add((handlerBuilder, operation, verb, route) => + options.RouteHandlerBuilders.Add((routeHandlerBuilder, operation, verb, route) => { - handlerBuilder.CacheOutput(policyBuilder => + // Initialize appHost and allServiceTypes + var appHost = HostContext.AppHost; + // Find the service matching the RequestType of the operation + var operationType = operation.RequestType; + // Match with operation, verb and route + appHost.Metadata.OperationsMap.TryGetValue(operationType, out var operationMap); + var serviceType = operationMap?.ServiceType; + if (serviceType == null) + return; + if (serviceType.HasAttributeOf()) { - policyBuilder.Cache().Expire(TimeSpan.FromSeconds(15)); - }); + // Handle duration from OutputCacheAttribute + var outputCacheAttribute = serviceType.FirstAttribute(); + routeHandlerBuilder.CacheOutput(policyBuilder => + { + policyBuilder.Cache().Expire(TimeSpan.FromSeconds(outputCacheAttribute.Duration)); + }); + } }); });