Skip to content

The Tags Property in OutputCache attribute is not working when using custom policies. #58785

Open
@skillmaker-dev

Description

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Hey, I'm trying to implement OutputCache with MultiTenancy Support, So I had to create a custom policy for that, I also used tags so that I can evict cache by tags + tenant id, this is how I'm doing it:

Custom OutputCache policy, MultitenantCachePolicy:

public class MultitenantCachePolicy : IOutputCachePolicy
{
    public ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellation)
    {
        var attemptOutputCaching = AttemptOutputCaching(context);
        context.EnableOutputCaching = true;
        context.AllowCacheLookup = attemptOutputCaching;
        context.AllowCacheStorage = attemptOutputCaching;
        context.AllowLocking = true;
        context.CacheVaryByRules.QueryKeys = "*";

        var tenantId = context.HttpContext.Items["tenantId"] as string ?? "";

        HashSet<string> newTags = [];

        foreach (var tag in context.Tags)
        {
            newTags.Add($"tenant:{tenantId}:{tag}");
        }

        newTags.Add($"tenant:{tenantId}");
        context.Tags.UnionWith(newTags);

        return ValueTask.CompletedTask;
    }

    public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation)
    {
        var response = context.HttpContext.Response;

        if (!StringValues.IsNullOrEmpty(response.Headers.SetCookie))
        {
            context.AllowCacheStorage = false;
            return ValueTask.CompletedTask;
        }

        if (response.StatusCode != StatusCodes.Status200OK)
        {
            context.AllowCacheStorage = false;
            return ValueTask.CompletedTask;
        }

        return ValueTask.CompletedTask;
    }

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

    private static bool AttemptOutputCaching(OutputCacheContext context)
    {
        var request = context.HttpContext.Request;

        if (!HttpMethods.IsGet(request.Method) && !HttpMethods.IsHead(request.Method))
        {
            return false;
        }

        return true;
    }
}

And this is how I register it:

services.AddOutputCache(options =>
{
    options.AddPolicy(CacheConfig.CachePolicies.MultiTenantCache, o =>
    {
        o.Expire(TimeSpan.FromMinutes(10))
            .VaryByValue(context => CacheExtension.VaryByTenantId(context))
            .AddPolicy<MultitenantCachePolicy>();
    });
}

And this is how I use it in controller endpoints:

[OutputCache(PolicyName = CacheConfig.CachePolicies.MultiTenantCache, Tags = [CacheConfig.CacheTags.DummyTag])]

The problem is that the DummyTagis not getting used at all, when i set a breakpoint in CacheRequestAsyncthe context Tags list is empty, however, if i set the Tag using this way:

options.AddPolicy(CacheConfig.CachePolicies.MultiTenantCache, o =>
    {
        o.Expire(TimeSpan.FromMinutes(10))
            .VaryByValue(context => CacheExtension.VaryByTenantId(context))
            .AddPolicy<MultitenantCachePolicy>()
            .Tag(CacheConfig.CacheTags.DummyTag);
    });

It works this way, what am I doing wrong here?

Expected Behavior

I should be able to register one custom policy, however, when I use that policy in the OutputCache attribute, I should be able to use the Tags property so that each controller endpoint has its Tag while sharing the same policy.

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

8.0.403

Anything else?

No response

Metadata

Assignees

No one assigned

    Labels

    area-middlewareIncludes: URL rewrite, redirect, response cache/compression, session, and other general middleswarefeature-output-caching

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions