Open sandboxFocusImprove this doc

LA0166: Async Interception Applied to a Method That Does Not Return a Task

Severity Error
Reported by PostSharp
Message Cannot apply the aspect "<Aspect>" to target "<Target>": async interception supports only methods that return Task or Task<T>. To intercept other methods, the aspect must implement OnInvoke; when it does, set the UnsupportedTargetAction property of the aspect to Fallback to intercept this method with OnInvoke, or to Ignore to skip it.

Cause

When a MethodInterceptionAspect overrides OnInvokeAsync, PostSharp intercepts async methods with this advice, which awaits the Task or Task<TResult> of the method. Async interception does not support the other async methods: those that return void, and those that return another awaitable type, such as ValueTask or ValueTask<TResult>. PostSharp reports LA0166 in these cases:

  • The target is such an async method, and the UnsupportedTargetAction of the aspect is Fail, its default value.
  • The aspect overrides OnInvokeAsync but not OnInvoke, and the target is a method that async interception does not support, async or not. Without OnInvoke, the aspect has no other way to intercept the method.

In the following example, the [Log] aspect is applied to an async method that returns a ValueTask<decimal>.

using System;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Serialization;

namespace Errors.LA0166_Error;

[PSerializable]
public class LogAttribute : MethodInterceptionAspect
{
    public override void OnInvoke( MethodInterceptionArgs args )
    {
        Console.WriteLine( $"Calling {args.Method.Name}." );
        args.Proceed();
    }

    public override async Task OnInvokeAsync( MethodInterceptionArgs args )
    {
        Console.WriteLine( $"Calling {args.Method.Name} asynchronously." );
        await args.ProceedAsync();
    }
}

public class PriceService
{
    // This async method returns a ValueTask, which async interception does not support.
    [Log]
    public async ValueTask<decimal> GetPriceAsync( string product )
    {
        await Task.Yield();

        return product.Length * 10m;
    }
}

public static class Program
{
    public static async Task Main()
    {
        decimal price = await new PriceService().GetPriceAsync( "Chair" );
        Console.WriteLine( $"Price: {price}" );
    }
}
error LA0166: LA0166_Error.cs(28,37): Cannot apply the aspect "Errors.LA0166_Error.LogAttribute" to target "Errors.LA0166_Error.PriceService.GetPriceAsync(System.String)": async interception supports only methods that return Task or Task<T>. To intercept other methods, the aspect must implement OnInvoke; when it does, set the UnsupportedTargetAction property of the aspect to Fallback to intercept this method with OnInvoke, or to Ignore to skip it.

Resolution

  • When the aspect overrides OnInvoke, set the UnsupportedTargetAction property of the aspect:
    • Fallback intercepts the method with OnInvoke. The advice then intercepts the call that starts the method and returns its awaitable object, not the asynchronous execution of the method.
    • Ignore does not apply the aspect to the method.
  • When the aspect overrides only OnInvokeAsync, override OnInvoke too, or do not apply the aspect to the method. Setting UnsupportedTargetAction alone does not help: Fallback reports LA0171 because there is no advice to fall back to.
  • Make the method return a Task or a Task<TResult>.

In the following example, the aspect falls back to OnInvoke for GetPriceAsync.

using System;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Serialization;

namespace Errors.LA0166_Fixed;

[PSerializable]
public class LogAttribute : MethodInterceptionAspect
{
    public override void OnInvoke( MethodInterceptionArgs args )
    {
        Console.WriteLine( $"Calling {args.Method.Name}." );
        args.Proceed();
    }

    public override async Task OnInvokeAsync( MethodInterceptionArgs args )
    {
        Console.WriteLine( $"Calling {args.Method.Name} asynchronously." );
        await args.ProceedAsync();
    }
}

public class PriceService
{
    // The aspect falls back to OnInvoke, which intercepts the call that returns the ValueTask.
    [Log( UnsupportedTargetAction = UnsupportedTargetAction.Fallback )]
    public async ValueTask<decimal> GetPriceAsync( string product )
    {
        await Task.Yield();

        return product.Length * 10m;
    }
}

public static class Program
{
    public static async Task Main()
    {
        decimal price = await new PriceService().GetPriceAsync( "Chair" );
        Console.WriteLine( $"Price: {price}" );
    }
}
Calling GetPriceAsync.
Price: 50

See Also

Reference

MethodInterceptionAspect
UnsupportedTargetAction
UnsupportedTargetAction

Other Resources

Intercepting Methods
Semantic Advising of Iterator and Async Methods