| Severity | Error |
| Reported by | PostSharp |
| Message | A cycle was detected while executing IAspectProvider instances. The following aspects may be part of the cycle: "<AspectTypes>". |
Cause
An aspect that implements IAspectProvider adds other aspects to the code with its ProvideAspects(object) method. PostSharp discovers aspects in rounds: it initializes the aspects found in a round, and the aspects that they provide form the next round. When the aspects keep providing new aspects for more than 64 rounds, PostSharp assumes that the providers form a cycle, stops, and reports LA0160.
The typical cause is two aspect providers that provide each other, or an aspect provider that provides a new instance of itself. Each round then provides new instances of the same aspects.
<AspectTypes> lists, separated by semicolons, the aspect types that implement IAspectProvider among the aspects of the last round. It may not name every aspect of the cycle. The message is reported on the first of these types.
In the following example, the [Audited] aspect provides the [Logged] aspect, which provides [Audited] again.
using System;
using System.Collections.Generic;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Serialization;
namespace Errors.LA0160_Error;
[PSerializable]
public class LogMethodAttribute : OnMethodBoundaryAspect
{
public override void OnEntry( MethodExecutionArgs args )
{
Console.WriteLine( $"Entering {args.Method.Name}" );
}
}
[PSerializable]
public class AuditedAttribute : TypeLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
{
// Audited types are also logged.
yield return new AspectInstance( targetElement, new LoggedAttribute() );
}
}
[PSerializable]
public class LoggedAttribute : TypeLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
{
Type type = (Type) targetElement;
foreach ( MethodInfo method in type.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly ) )
{
yield return new AspectInstance( method, new LogMethodAttribute() );
}
// Logged types are also audited: each aspect provides the other one again, without end.
yield return new AspectInstance( targetElement, new AuditedAttribute() );
}
}
[Audited]
public class OrderService
{
public void PlaceOrder( string product )
{
Console.WriteLine( $"Ordered {product}" );
}
}
public static class Program
{
public static void Main()
{
new OrderService().PlaceOrder( "Coffee" );
}
}
error LA0160: LA0160_Error.cs(29,14): A cycle was detected while executing IAspectProvider instances. The following aspects may be part of the cycle: "Errors.LA0160_Error.LoggedAttribute".
Resolution
Make sure that the chain of provided aspects ends: an aspect must not provide, directly or through other aspects, an aspect of its own type on the same target. When two aspects should always go together, let one of them provide the other, and apply only that one.
In the following example, [Logged] no longer provides [Audited], so the aspects provided by [Audited] log the call of PlaceOrder.
using System;
using System.Collections.Generic;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Serialization;
namespace Errors.LA0160_Fixed;
[PSerializable]
public class LogMethodAttribute : OnMethodBoundaryAspect
{
public override void OnEntry( MethodExecutionArgs args )
{
Console.WriteLine( $"Entering {args.Method.Name}" );
}
}
[PSerializable]
public class AuditedAttribute : TypeLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
{
// Audited types are also logged.
yield return new AspectInstance( targetElement, new LoggedAttribute() );
}
}
[PSerializable]
public class LoggedAttribute : TypeLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
{
Type type = (Type) targetElement;
foreach ( MethodInfo method in type.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly ) )
{
yield return new AspectInstance( method, new LogMethodAttribute() );
}
// This aspect no longer provides AuditedAttribute, so the chain of provided aspects ends here.
}
}
[Audited]
public class OrderService
{
public void PlaceOrder( string product )
{
Console.WriteLine( $"Ordered {product}" );
}
}
public static class Program
{
public static void Main()
{
new OrderService().PlaceOrder( "Coffee" );
}
}
Entering PlaceOrder
Ordered Coffee
See Also
Reference
IAspectProvider
AspectInstance
Other Resources