Open sandboxFocusImprove this doc

PS0112: Aspect Conflict or Unsatisfied Aspect Requirement

Severity Error
Reported by PostSharp
Message Conflicting aspects on "<Declaration>": <Reason>.

Cause

The dependencies of an aspect class, AspectTypeDependencyAttribute, AspectRoleDependencyAttribute and AspectEffectDependencyAttribute, can do more than order aspects. PostSharp checks them once it has ordered the aspects of a declaration, and reports PS0112 in two cases:

  • A dependency with the Conflict action matches another aspect on the same declaration: another aspect type, another aspect that provides a role, or another aspect that has an effect. With a position, the conflict exists only when the other aspect runs before (After) or after (Before) the current one. <Reason> then says that the first transformation cannot be used together with, cannot be preceded by or cannot be followed by the second.
  • A dependency with the Require action matches no other aspect on the declaration, or none at the required position. <Reason> then says that the transformation must be used together with, must be preceded by or must be followed by a transformation that matches the condition.

Dependencies between transformations of the same aspect instance are ignored. PS0112 is an error, or a warning when the IsWarning property of the dependency is true.

In the following example, both caching aspects conflict with any other aspect that provides the caching role, and both are applied to GetProductName.

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

namespace Errors.PS0112_Error;

// A method can have only one caching aspect.
[PSerializable]
[ProvideAspectRole( StandardRoles.Caching )]
[AspectRoleDependency( AspectDependencyAction.Conflict, StandardRoles.Caching )]
public class MemoryCacheAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( "Look up the memory cache" );
}

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

public class ProductCatalog
{
    [MemoryCache]
    [DiskCache] // A second caching aspect on the same method.
    public string GetProductName( int id )
    {
        return id == 1 ? "Coffee" : "Tea";
    }
}

public static class Program
{
    public static void Main()
    {
        Console.WriteLine( new ProductCatalog().GetProductName( 1 ) );
    }
}
error PS0112: PS0112_Error.cs(29,19): Conflicting aspects on "Errors.PS0112_Error.ProductCatalog.GetProductName(System.Int32)": 'Errors.PS0112_Error.MemoryCacheAttribute: Marker BEFORE (2)' cannot be used together with 'Errors.PS0112_Error.DiskCacheAttribute: Marker BEFORE (3)' because the second provides the role 'Caching'.

Resolution

  • For a conflict, remove one of the conflicting aspects from the declaration, or exclude it from this declaration with AttributeExclude when the aspect is multicast. When the conflict depends on a position, change the order of the aspects so that the conflicting aspect is on the allowed side.
  • For a requirement, add the aspect that the dependency requires, at the required position.
  • When the aspects are yours and the combination is valid, remove the dependency, or give it a position that describes the actual problem.

In the following example, the method keeps only the memory cache.

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

namespace Errors.PS0112_Fixed;

// A method can have only one caching aspect.
[PSerializable]
[ProvideAspectRole( StandardRoles.Caching )]
[AspectRoleDependency( AspectDependencyAction.Conflict, StandardRoles.Caching )]
public class MemoryCacheAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( "Look up the memory cache" );
}

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

public class ProductCatalog
{
    [MemoryCache] // The only caching aspect of the method.
    public string GetProductName( int id )
    {
        return id == 1 ? "Coffee" : "Tea";
    }
}

public static class Program
{
    public static void Main()
    {
        Console.WriteLine( new ProductCatalog().GetProductName( 1 ) );
    }
}
Look up the memory cache
Coffee

See Also

Reference

AspectDependencyAction
AspectRoleDependencyAttribute
ProvideAspectRoleAttribute

Other Resources

Coping with Several Aspects on the Same Target