Open sandboxFocusImprove this doc

CAC013: Cache Aspect on an Unsupported Async Method

Severity Error
Reported by PostSharp.Patterns.Caching
Message Cannot add the [Cache] aspect to the <Method> method because it is an async iterator, or an async method that does not return Task<T>, such as ValueTask<T>, which the asynchronous advice does not support. To cache the result of an async method, make it return Task<T>. Otherwise, set the UnsupportedTargetAction property of [Cache] to Ignore to skip the method, or to Fallback to cache the object that the method returns, if this object can be consumed several times.

Cause

The CacheAttribute aspect caches the result of an async method only when the method returns Task<TResult>. It does not support the following methods:

For such a method, the synchronous advice could only cache the object that the method returns, and a ValueTask<TResult> may be awaited only once. PostSharp therefore reports CAC013 when the <xref:PostSharp.Patterns.Caching.CacheAttribute.UnsupportedTargetAction> property of the aspect has its default value, Default.

A method that returns a ValueTask<TResult> or an IAsyncEnumerable<T> without the async keyword is not affected: the aspect caches the object that it returns.

In the following example, GetTemperatureAsync is an async method that returns ValueTask<int>.

using System;
using System.Threading.Tasks;
using PostSharp.Patterns.Caching;
using PostSharp.Patterns.Caching.Backends;

namespace Errors.CAC013_Error;

public class WeatherService
{
    [Cache]
    public async ValueTask<int> GetTemperatureAsync( string city ) // [Cache] does not support async methods that return ValueTask<T>.
    {
        Console.WriteLine( $"Querying the temperature of {city}." );
        await Task.Delay( 10 );

        return 21;
    }
}

public static class Program
{
    public static async Task Main()
    {
        CachingServices.DefaultService.Backend = new MemoryCachingBackend();

        var service = new WeatherService();
        Console.WriteLine( await service.GetTemperatureAsync( "Prague" ) );
        Console.WriteLine( await service.GetTemperatureAsync( "Prague" ) );
    }
}
error CAC013: CAC013_Error.cs(11,33): Cannot add the [Cache] aspect to the Errors.CAC013_Error.WeatherService.GetTemperatureAsync(System.String) method because it is an async iterator, or an async method that does not return Task<T>, such as ValueTask<T>, which the asynchronous advice does not support. Set the UnsupportedTargetAction property of [Cache] to Fallback to cache the object that the method returns, or to Ignore to skip the method.

Resolution

  • Make the method return Task<TResult>, so that the aspect caches its result.
  • To leave the method without caching, set the <xref:PostSharp.Patterns.Caching.CacheAttribute.UnsupportedTargetAction> property of the aspect to Ignore: the calls to the method then proceed without the cache.
  • To cache the object that the method returns, set it to Fallback. Do this only when this object can be consumed several times, which is not the case of most ValueTask<TResult> objects.

In the following example, GetTemperatureAsync returns Task<int>, and the second call reads the temperature from the cache.

using System;
using System.Threading.Tasks;
using PostSharp.Patterns.Caching;
using PostSharp.Patterns.Caching.Backends;

namespace Errors.CAC013_Fixed;

public class WeatherService
{
    [Cache]
    public async Task<int> GetTemperatureAsync( string city ) // The method returns Task<T>, which [Cache] supports.
    {
        Console.WriteLine( $"Querying the temperature of {city}." );
        await Task.Delay( 10 );

        return 21;
    }
}

public static class Program
{
    public static async Task Main()
    {
        CachingServices.DefaultService.Backend = new MemoryCachingBackend();

        var service = new WeatherService();
        Console.WriteLine( await service.GetTemperatureAsync( "Prague" ) );
        Console.WriteLine( await service.GetTemperatureAsync( "Prague" ) );
    }
}
Querying the temperature of Prague.
21
21

See Also

Reference

CacheAttribute
<xref:PostSharp.Patterns.Caching.CacheAttribute.UnsupportedTargetAction>
UnsupportedTargetAction

Other Resources

Caching Method Return Values