| 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:
- an async method, declared with the
asynckeyword, that returns another type, such as ValueTask<TResult>; - an async iterator, which returns IAsyncEnumerable<T> or IAsyncEnumerator<T> and contains
awaitandyield return.
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