Open sandboxFocusImprove this doc

PS0111: Aspect Dependencies Form a Cycle

Severity Error
Reported by PostSharp
Message Conflicting aspects on "<Declaration>": dependencies between transformations form a cycle. Members of this cycle are: <CycleMembers> Details of dependencies: <Details>

Cause

When several aspects apply to the same declaration, PostSharp orders their transformations according to the dependencies of the aspect classes: AspectTypeDependencyAttribute, AspectRoleDependencyAttribute and AspectEffectDependencyAttribute with the Order or Require action and a position, and the AspectPriority of the aspects. PostSharp reports PS0111 when these dependencies form a cycle: each transformation of the cycle must come before the next one, and the last one before the first one, so no order satisfies them.

<CycleMembers> lists the transformations of the cycle, one per line. <Details> lists, for each of them, its dependencies and the roles that it provides. The message is therefore written on several lines.

In the following example, AuthorizeAttribute runs before the caching role, CacheAttribute before the tracing role, and TraceAttribute before the security role, which AuthorizeAttribute provides.

using System;
using PostSharp.Aspects;
using PostSharp.Aspects.Dependencies;
using PostSharp.Serialization;

namespace Errors.PS0111_Error;

[PSerializable]
[ProvideAspectRole( StandardRoles.Security )]
[AspectRoleDependency( AspectDependencyAction.Order, AspectDependencyPosition.Before, StandardRoles.Caching )]
public class AuthorizeAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( "Authorize" );
}

[PSerializable]
[ProvideAspectRole( StandardRoles.Caching )]
[AspectRoleDependency( AspectDependencyAction.Order, AspectDependencyPosition.Before, StandardRoles.Tracing )]
public class CacheAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( "Look up the cache" );
}

[PSerializable]
[ProvideAspectRole( StandardRoles.Tracing )]
[AspectRoleDependency( AspectDependencyAction.Order, AspectDependencyPosition.Before, StandardRoles.Security )] // Closes the cycle: Security before Caching before Tracing before Security.
public class TraceAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( "Trace" );
}

public class ReportService
{
    [Authorize]
    [Cache]
    [Trace]
    public void GetReport()
    {
        Console.WriteLine( "Build the report" );
    }
}

public static class Program
{
    public static void Main()
    {
        new ReportService().GetReport();
    }
}
error PS0111: PS0111_Error.cs(37,17): .
error PS0111: PS0111_Error.cs(37,17): Action=Order, Position=Before, Condition={Role=Caching}
error PS0111: PS0111_Error.cs(37,17): Action=Order, Position=Before, Condition={Role=Security}
error PS0111: PS0111_Error.cs(37,17): Action=Order, Position=Before, Condition={Role=Tracing}
error PS0111: PS0111_Error.cs(37,17): Conflicting aspects on "Errors.PS0111_Error.ReportService.GetReport": dependencies between transformations form a cycle. Members of this cycle are:
error PS0111: PS0111_Error.cs(37,17): Details of dependencies:
error PS0111: PS0111_Error.cs(37,17): Errors.PS0111_Error.AuthorizeAttribute: Marker AFTER (3)
error PS0111: PS0111_Error.cs(37,17): Errors.PS0111_Error.AuthorizeAttribute: Marker AFTER (3):
error PS0111: PS0111_Error.cs(37,17): Errors.PS0111_Error.CacheAttribute: Marker AFTER (4)
error PS0111: PS0111_Error.cs(37,17): Errors.PS0111_Error.CacheAttribute: Marker AFTER (4):
error PS0111: PS0111_Error.cs(37,17): Errors.PS0111_Error.TraceAttribute: Marker AFTER (5)
error PS0111: PS0111_Error.cs(37,17): Errors.PS0111_Error.TraceAttribute: Marker AFTER (5):
error PS0111: PS0111_Error.cs(37,17): Provide Role=Caching
error PS0111: PS0111_Error.cs(37,17): Provide Role=Security
error PS0111: PS0111_Error.cs(37,17): Provide Role=Tracing

Resolution

Find in <Details> the dependency that contradicts the order you want, and change or remove it:

  • When the aspect classes are yours, reverse the position of the wrong dependency, or remove it when another dependency already orders the aspects.
  • When one aspect comes from a third party, you cannot change its dependencies, but you can change those of your own aspects, or add a dependency at assembly level with the TargetType property.

In the following example, TraceAttribute runs after the security role, which agrees with the other two dependencies.

using System;
using PostSharp.Aspects;
using PostSharp.Aspects.Dependencies;
using PostSharp.Serialization;

namespace Errors.PS0111_Fixed;

[PSerializable]
[ProvideAspectRole( StandardRoles.Security )]
[AspectRoleDependency( AspectDependencyAction.Order, AspectDependencyPosition.Before, StandardRoles.Caching )]
public class AuthorizeAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( "Authorize" );
}

[PSerializable]
[ProvideAspectRole( StandardRoles.Caching )]
[AspectRoleDependency( AspectDependencyAction.Order, AspectDependencyPosition.Before, StandardRoles.Tracing )]
public class CacheAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( "Look up the cache" );
}

[PSerializable]
[ProvideAspectRole( StandardRoles.Tracing )]
[AspectRoleDependency( AspectDependencyAction.Order, AspectDependencyPosition.After, StandardRoles.Security )] // Agrees with the other dependencies.
public class TraceAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( "Trace" );
}

public class ReportService
{
    [Authorize]
    [Cache]
    [Trace]
    public void GetReport()
    {
        Console.WriteLine( "Build the report" );
    }
}

public static class Program
{
    public static void Main()
    {
        new ReportService().GetReport();
    }
}
Authorize
Look up the cache
Trace
Build the report

See Also

Reference

AspectRoleDependencyAttribute
AspectTypeDependencyAttribute
AspectDependencyPosition

Other Resources

Coping with Several Aspects on the Same Target
Ordering Advices