摆脱 OnMethodBoundaryAspect 和 ILocationValidationAspect 之间的方面冲突警告?

Get rid of aspect conflict warnings between OnMethodBoundaryAspect and ILocationValidationAspect?

用于工具和库的 PostSharp 6.7.12 版。我定义了一些自定义属性:

[PSerializable]
public sealed class CheckInvariantAttribute : OnMethodBoundaryAspect
{

    // Snip - build-time logic here...

    public override void OnExit(MethodExecutionArgs args)
    {
        // Check stuff...
    }
}

[Serializable]
public sealed class NotDefaultAttribute : LocationContractAttribute,
    ILocationValidationAspect<Guid>,
    ILocationValidationAspect<DateTime>,
    IValidableAnnotation
{
    // Snip - build-time logic here...

    public Exception ValidateValue(
        DateTime value, 
        string locationName, 
        LocationKind locationKind, 
        LocationValidationContext contect)
    {
        // Snip, do stuff...
    }

    public Exception ValidateValue(
        Guid value, 
        string locationName, 
        LocationKind locationKind, 
        LocationValidationContext contect)
    {
        // Snip, do stuff...
    }
}

...而且我还在使用其他可用的代码合同属性。我将这两个应用于相同的方法:

[CheckInvariant]
public virtual void DoSomething(
    [NotNull] SomeObjectType inst,
    [NotDefault] DateTime someVal,
    [StrictlyPositive] Decimal someAmt)
{
        // Snip, do stuff...
}

当我这样做时,我收到一条编译时警告,指出 Conflicting aspects on "My.Namespace.MyClass.DoSomething( /* insert parameters here */ )": transformations "My.Namespace.NotDefaultAttribute: Validates the value passed to parameter 'someVal' and "My.Namespace.CheckInvariantAttribute: Wrapped by advice(s) OnExit" are not commutative, but they are not strongly ordered. Their order of execution is undeterministic.

首先,我希望这些应该是确定性的——一个将方面应用于参数,而另一个在方法退出时应用。这里有一个自然的秩序。除此之外,我试过将它们标记为可交换的,我试过按角色或优先级对它们进行排序,但在所有情况下都失败了。 (我可能做错了。)

如何消除这些警告?

这是此示例位置的构建输出:

1>C:\MyProject\SomeClass.cs(159,23,159,27): warning PS0114: Conflicting aspects on "My.Namespace.MyClass.DoSomething(SometObjectType, System.DateTime, System.Decimal)": transformations "My.Namespace.NotDefaultAttribute: Validates the value passed to parameter 'someVal'" and "My.Namespace.CheckInvariantAttribute: Wrapped by advice(s) OnExit" are not commutative, but they are not strongly ordered. Their order of execution is undeterministic.
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Detail of dependencies for the previous warnings: 
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.CheckInvariantAttribute: Marker BEFORE (54):
1>Namespace\MyClass.cs(159,23,159,27): message PS0124:    (no dependency)
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: PostSharp.Patterns.Contracts.NotNullAttribute: Validates the value passed to parameter 'inst':
1>Namespace\MyClass.cs(159,23,159,27): message PS0124:    Provide Role=Validation
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.NotDefaultAttribute: Validates the value passed to parameter 'someVal':
1>Namespace\MyClass.cs(159,23,159,27): message PS0124:    Provide Role=Validation
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.CheckInvariantAttribute: Wrapped by advice(s) OnExit:
1>Namespace\MyClass.cs(159,23,159,27): message PS0124:    Action=Order, Position=After, Condition={equals "My.Namespace.CheckInvariantAttribute: Marker BEFORE (54)"}
1>Namespace\MyClass.cs(159,23,159,27): message PS0124:    Action=Order, Position=Before, Condition={equals "My.Namespace.CheckInvariantAttribute: Marker AFTER (54)"}
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: PostSharp.Patterns.Contracts.StrictlyPositiveAttribute: Validates the value passed to parameter 'someAmt':
1>Namespace\MyClass.cs(159,23,159,27): message PS0124:    Provide Role=Validation
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.CheckInvariantAttribute: Marker AFTER (54):
1>Namespace\MyClass.cs(159,23,159,27): message PS0124:    (no dependency)

虽然属性 [NotDefault] 应用于参数,但实际转换应用于方法体。它基本上与 [CheckInvariant] 方面相同的 OnMethodBoundary 转换。您可以想象转换后的方法体具有以下结构:

NotDefaultOnEntry
try
{
  CheckInvariantOnEntry
  try
  {
    // original method body
    CheckInvariantOnSuccess
  }
  catch
  {
    CheckInvariantOnException
  }
  finally
  {
    CheckInvariantOnExit
  }
  NotDefaultOnSuccess
}
catch
{
  NotDefaultOnException
}
finally
{
  NotDefaultOnExit
}

如您所见,方面的顺序将影响方法主体包装的应用顺序。当然,在您的特定情况下,这些方面并没有提供所有可能的建议,最终的方法体结构更简单。在这种情况下,PostSharp 仍然必须发出警告以避免意外和意外行为。

您可以使用方面角色依赖关系来对这些方面进行排序。将 [ProvideAspectRole] 应用到 NotDefaultAttribute 并将 [AspectRoleDependency] 应用到 CheckInvariantAttribute,如下所示。

[Serializable]
[ProvideAspectRole(StandardRoles.Validation)]
public sealed class NotDefaultAttribute : LocationContractAttribute,
    ILocationValidationAspect<Guid>,
    ILocationValidationAspect<DateTime>,
    IValidableAnnotation
{
    // ...
}

[PSerializable]
[AspectRoleDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, StandardRoles.Validation)]
public sealed class CheckInvariantAttribute : OnMethodBoundaryAspect
{
    // ...
}