Open sandboxFocusImprove this doc

LA0196: Aspect Cannot Semantically Advise an Async Iterator

Severity Error
Reported by PostSharp
Message Cannot apply aspect "<Aspect>" to method "<Method>": this aspect does not support semantic advising of C# 8 async iterator methods. Set the UnsupportedTargetAction property of the aspect to Fallback or Ignore to suppress this error.

Cause

An async iterator is a method that is both async and an iterator: it returns IAsyncEnumerable<T> or IAsyncEnumerator<T> and contains both await and yield return. By default, PostSharp applies method-level aspects to such a method semantically, that is, to the code that you wrote rather than to the method that creates the enumerator (see Semantic Advising of Iterator and Async Methods). Some advices are not supported by semantic advising of async iterators:

When the aspect implements one of these advices and its UnsupportedTargetAction property is Fail, which is the default, PostSharp reports LA0196 on the target method instead of applying the aspect. When the aspect defines its advices as advice methods instead of overriding them, PostSharp reports LA0197.

In the following example, the [Log] aspect implements OnYield and OnResume, and is applied to an async iterator.

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

namespace Errors.LA0196_Error;

[PSerializable]
public class LogAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( $"Entering {args.Method.Name}." );

    public override void OnYield( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} is waiting." );

    public override void OnResume( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} resumes." );

    public override void OnExit( MethodExecutionArgs args ) => Console.WriteLine( $"Leaving {args.Method.Name}." );
}

public class OrderService
{
    // The aspect has OnYield and OnResume advices, which PostSharp cannot apply to an async iterator.
    [Log]
    public async IAsyncEnumerable<int> GetOrderIdsAsync()
    {
        for ( var id = 1; id <= 2; id++ )
        {
            await Task.Delay( 10 );

            yield return id;
        }
    }
}

public static class Program
{
    public static async Task Main()
    {
        await foreach ( var id in new OrderService().GetOrderIdsAsync() )
        {
            Console.WriteLine( $"Order {id}" );
        }
    }
}
error LA0196: LA0196_Error.cs(25,40): Cannot apply aspect "Errors.LA0196_Error.LogAttribute" to method "Errors.LA0196_Error.OrderService.GetOrderIdsAsync": this aspect does not support semantic advising of C# 8 async iterator methods. Set the UnsupportedTargetAction property of the aspect to Fallback or Ignore to suppress this error.

Resolution

  • Set the UnsupportedTargetAction property of the aspect, where you apply it or in its constructor:
    • Fallback applies the aspect non-semantically: the aspect runs when the method is called and creates the async enumerator, not during the enumeration, and OnYield and OnResume are not called. A MethodInterceptionAspect then intercepts the method with its OnInvoke advice.
    • Ignore skips the aspect for this method.
  • For an OnMethodBoundaryAspect, remove the OnYield and OnResume advices if you do not need them: PostSharp then applies the other advices semantically to the async iterator.
  • Exclude async iterators from the methods that the aspect targets.

In the following example, the aspect falls back to non-semantic advising. Its OnEntry and OnExit advices run before the enumeration starts.

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

namespace Errors.LA0196_Fixed;

[PSerializable]
public class LogAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( $"Entering {args.Method.Name}." );

    public override void OnYield( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} is waiting." );

    public override void OnResume( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} resumes." );

    public override void OnExit( MethodExecutionArgs args ) => Console.WriteLine( $"Leaving {args.Method.Name}." );
}

public class OrderService
{
    // The aspect falls back to non-semantic advising for this async iterator.
    [Log( UnsupportedTargetAction = UnsupportedTargetAction.Fallback )]
    public async IAsyncEnumerable<int> GetOrderIdsAsync()
    {
        for ( var id = 1; id <= 2; id++ )
        {
            await Task.Delay( 10 );

            yield return id;
        }
    }
}

public static class Program
{
    public static async Task Main()
    {
        await foreach ( var id in new OrderService().GetOrderIdsAsync() )
        {
            Console.WriteLine( $"Order {id}" );
        }
    }
}
Entering GetOrderIdsAsync.
Leaving GetOrderIdsAsync.
Order 1
Order 2

See Also

Reference

UnsupportedTargetAction
UnsupportedTargetAction
OnMethodBoundaryAspect
MethodInterceptionAspect

Other Resources

Semantic Advising of Iterator and Async Methods