Open sandboxFocusImprove this doc

LA0160: Aspect Providers Provide Aspects Without End

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

Adding Aspects Dynamically