| Severity | Error |
| Reported by | PostSharp |
| Message | Cannot apply advice "<Advice>" to method "<Method>": this advice does not support semantic advising of C# 8 async iterator methods. Set the UnsupportedTargetAction property of the master advice to Fallback or Ignore to suppress this error. |
Cause
LA0197 is the counterpart of LA0196 for aspects built from advice methods (see Advice Methods). 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 advices to such a method semantically (see Semantic Advising of Iterator and Async Methods), but semantic advising of async iterators does not support some advices:
- OnMethodYieldAdvice and OnMethodResumeAdvice, in a group of method boundary advices;
- OnMethodInvokeAsyncAdvice, in a group of method interception advices.
When the group of advices contains one of them and the UnsupportedTargetAction property of its master advice, for example UnsupportedTargetAction, is Fail, which is the default, PostSharp reports LA0197 on the target method. <Advice> is the master advice of the group.
In the following example, the [Log] aspect has an OnMethodYieldAdvice and an OnMethodResumeAdvice in the group of its OnEntry advice, and is applied to an async iterator.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;
namespace Errors.LA0197_Error;
[PSerializable]
public class LogAttribute : MethodLevelAspect
{
[OnMethodEntryAdvice]
public void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( $"Entering {args.Method.Name}." );
[OnMethodYieldAdvice]
public void OnYield( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} is waiting." );
[OnMethodResumeAdvice]
public void OnResume( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} resumes." );
[OnMethodExitAdvice]
public void OnExit( MethodExecutionArgs args ) => Console.WriteLine( $"Leaving {args.Method.Name}." );
}
public class OrderService
{
// The advices of the aspect include OnYield and OnResume, 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 LA0197: LA0197_Error.cs(30,40): Cannot apply advice "Errors.LA0197_Error.LogAttribute.OnEntry(PostSharp.Aspects.MethodExecutionArgs)" to method "Errors.LA0197_Error.OrderService.GetOrderIdsAsync": this advice does not support semantic advising of C# 8 async iterator methods. Set the UnsupportedTargetAction property of the master advice to Fallback or Ignore to suppress this error.
Resolution
- Set the
UnsupportedTargetActionproperty of the master advice. PostSharp reads it from the master advice only, and the advice that sets it becomes the master advice of the group: - For method boundary advices, remove the yield and resume advices if you do not need them: PostSharp then applies the other advices semantically to the async iterator.
- Exclude async iterators from the pointcut of the advices.
In the following example, the advices fall back to non-semantic advising. The OnEntry and OnExit advices run before the enumeration starts.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;
namespace Errors.LA0197_Fixed;
[PSerializable]
public class LogAttribute : MethodLevelAspect
{
// The advices fall back to non-semantic advising for async iterators.
[OnMethodEntryAdvice( UnsupportedTargetAction = UnsupportedTargetAction.Fallback )]
public void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( $"Entering {args.Method.Name}." );
[OnMethodYieldAdvice]
public void OnYield( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} is waiting." );
[OnMethodResumeAdvice]
public void OnResume( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} resumes." );
[OnMethodExitAdvice]
public void OnExit( MethodExecutionArgs args ) => Console.WriteLine( $"Leaving {args.Method.Name}." );
}
public class OrderService
{
[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}" );
}
}
}
Entering GetOrderIdsAsync.
Leaving GetOrderIdsAsync.
Order 1
Order 2
See Also
Reference
UnsupportedTargetAction
UnsupportedTargetAction
OnMethodYieldAdvice
OnMethodResumeAdvice
OnMethodInvokeAsyncAdvice
Other Resources
Advice Methods
Semantic Advising of Iterator and Async Methods