c# - .NET 7 创建自定义输出缓存策略

标签 c# .net caching outputcache .net-7.0

我目前正在实现 .NET 7 框架的一些新功能。其中一部分与新的缓存机制有关。

启动时我已经配置了缓存:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHealthChecks();
builder.Services.AddCors();
builder.Services.AddOutputCache();
//builder.Services.InitializeApplication(builder.Configuration);

var app = builder.Build();

app.UseOutputCache();
app.UseCors();
//app.UseAuthentication();
//app.UseAuthorization();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapGroup("reports-api/requests")
    .MapRequestsApi();

app.MapHealthChecks("/healthcheck");

app.Run();

路由组如下所示:

public static class RequestsEndpoint
{
    public static RouteGroupBuilder MapRequestsApi(this RouteGroupBuilder group)
    {
        group.MapGet("/all", async (IMediator mediator) =>
            {
                await Task.Delay(2000);
                return await mediator.Send(new RetrieveRequestsQuery());
            })
            .CacheOutput(x => x.Tag("requests"));

        group.MapPost("/add", async (string details, IMediator mediator, IOutputCacheStore store) =>
        { 
            await mediator.Send(new AddRequestCommand { Details = details });
            await store.EvictByTagAsync("requests", CancellationToken.None);

        });

        group.MapGet("/{id}", async (Guid requestId, IMediator mediator) =>
        {
            await mediator.Send(new RetrieveRequestDetailsQuery()
            {
                Id = requestId
            });
        });

        //group.MapPut("/{id}", UpdateRequest);
        //group.MapDelete("/{id}", DeleteRequest);

        return group;
    }
}

当我想从缓存中提供请求列表或当我想逐出缓存(列表中的新项目)时,带有标签的缓存机制可以正常工作

但是,我希望每个项目都有某种缓存 - 当我根据 ID 检索请求时,我只想缓存这些值,直到通过 PUT 或 PATCH 更改详细信息为止。

我能做的就是注册 具有相同标签(“requests”)的 group.MapGet("/{id}") 端点。但是,如果有更新,我将被迫驱逐所有内容。这并不理想。

我正在观看此视频 ( Output Cache Microsoft ),他们正在查看名为 DefaultOutputCachePolicy 的内容

我只能在.NET 7中找到接口(interface):IOutputCachePolicy,它要求我实现以下方法:

public class ByIdCachePolicy : IOutputCachePolicy
{
    public ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellation) => throw new NotImplementedException();

    public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation) => throw new NotImplementedException();

    public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation) => throw new NotImplementedException();
}

文档中没有关于如何实现这一点的线索,而且我找不到默认策略的代码。我们应该如何实现这个接口(interface)?

最佳答案

我设法通过解决方法实现了我想要的目标:

 group.MapGet("/{id}", async (Guid requestId, IMediator mediator) =>
        {
            await Task.Delay(2000);
            return await mediator.Send(new RetrieveRequestDetailsQuery
            {
                Id = requestId
            });
        })
        .CacheOutput(cachePolicyBuilder => cachePolicyBuilder.With(context =>
        {
            if (context.HttpContext.Request.QueryString.Value != null)
            {
              var queryParams =  HttpUtility.ParseQueryString(context.HttpContext.Request.QueryString.Value);
                context.Tags.Add(queryParams["requestId"]!);
            }

            return true;
        }));

    group.MapPut("/{id}",
        async (Guid id, IOutputCacheStore store, CancellationToken ct) =>
            await store.EvictByTagAsync(id.ToString(), CT));

使用cachePolicyBuilder With方法,我可以访问缓存上下文,并可以查看查询字符串并添加自定义标记(按ID)。

但是,这并不简单,因为 With 函数允许您过滤缓存的请求。我在这里所做的始终是返回 true,但是还添加了自定义标签。

不理想,但它有效......

编辑

经过进一步研究后,我最终使用了以下策略:

public class ByIdCachePolicy : IOutputCachePolicy
{
    public ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellation)
    {
        var idRoute = context.HttpContext.Request.RouteValues["id"];
        if (idRoute == null)
        {
            return ValueTask.CompletedTask;
        }
        context.Tags.Add(idRoute.ToString()!);
        return ValueTask.CompletedTask;
    }

    public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation)=> ValueTask.CompletedTask;

    public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation) => ValueTask.CompletedTask;
}

像这样:

group.MapGet("/{id}", async (Guid id, IMediator mediator) =>
    {
        await Task.Delay(2000);
        return await mediator.Send(new RetrieveRequestDetailsQuery
        {
            Id = id
        });
    }).CacheOutput(x => x.AddPolicy<ByIdCachePolicy>());

关于c# - .NET 7 创建自定义输出缓存策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74610528/

相关文章:

c# - 未创建 Streamwriter 文件

c - 在生产者/消费者多线程环境中优化共享缓冲区

javascript - Play Framework 2.1 返回 HTTP header 而不是 JavaScript

c# - 关于使用 RuntimeHelpers.PrepareMethod() 进行预 JIT 的问题

c# - Xamarin.Forms 汉堡包菜单图标在更新到 Xamarin.Forms 2.2 后消失

c# - 将当前查看的 Outlook 电子邮件正文存储到字符串中

c# - 如何通过扩展方法隐藏成员方法

c# - 我读错了平衡括号挑战吗?

c# - 如何获取 Controller 的 POST 操作方法?

javascript - 防止html页面浏览缓存