dotnet / dotnet/aspnetcore

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

Open
#58,785 3 comments 0 reactions 0 assignees View on GitHub
area-middleware feature-output-caching
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [x] 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 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();
});
}
```
And this is how I use it in controller endpoints:

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

The problem is that the `DummyTag`is not getting used at all, when i set a breakpoint in `CacheRequestAsync`the 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()
.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_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.