File: Policies\PredicatePolicy.cs
Web Access
Project: src\src\Middleware\OutputCaching\src\Microsoft.AspNetCore.OutputCaching.csproj (Microsoft.AspNetCore.OutputCaching)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
namespace Microsoft.AspNetCore.OutputCaching.Policies;
 
/// <summary>
/// A policy that adds a requirement to another policy.
/// </summary>
internal sealed class PredicatePolicy : IOutputCachePolicy
{
    // TODO: Accept a non async predicate too?
 
    private readonly Func<OutputCacheContext, ValueTask<bool>> _predicate;
    private readonly IOutputCachePolicy _policy;
 
    /// <summary>
    /// Creates a new <see cref="PredicatePolicy"/> instance.
    /// </summary>
    /// <param name="asyncPredicate">The predicate.</param>
    /// <param name="policy">The policy.</param>
    public PredicatePolicy(Func<OutputCacheContext, ValueTask<bool>> asyncPredicate, IOutputCachePolicy policy)
    {
        _predicate = asyncPredicate;
        _policy = policy;
    }
 
    /// <inheritdoc />
    ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
    {
        return ExecuteAwaited(static (policy, context, cancellationToken) => policy.CacheRequestAsync(context, cancellationToken), _policy, context, cancellationToken);
    }
 
    /// <inheritdoc />
    ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
    {
        return ExecuteAwaited(static (policy, context, cancellationToken) => policy.ServeFromCacheAsync(context, cancellationToken), _policy, context, cancellationToken);
    }
 
    /// <inheritdoc />
    ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
    {
        return ExecuteAwaited(static (policy, context, cancellationToken) => policy.ServeResponseAsync(context, cancellationToken), _policy, context, cancellationToken);
    }
 
    private ValueTask ExecuteAwaited(Func<IOutputCachePolicy, OutputCacheContext, CancellationToken, ValueTask> action, IOutputCachePolicy policy, OutputCacheContext context, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(action);
 
        if (_predicate == null)
        {
            return action(policy, context, cancellationToken);
        }
 
        var task = _predicate(context);
 
        if (task.IsCompletedSuccessfully)
        {
            if (task.Result)
            {
                return action(policy, context, cancellationToken);
            }
 
            return ValueTask.CompletedTask;
        }
 
        return Awaited(task);
 
        async ValueTask Awaited(ValueTask<bool> task)
        {
            if (await task)
            {
                await action(policy, context, cancellationToken);
            }
        }
    }
}