Getting "Error LA0145: Incorrect ordering of advices on method" When Updating PostSharp

ON's Avatar

ON

11 Jul, 2017 06:52 PM

Hello,

I keep getting an error regarding the ordering of advices and I'm a bit stumped as to why it's happening. It's only occuring when I try and update the PostSharp version to 4.3.31.

I have methods that are using the NotNull attribute, like so:

[return: NotNull]
protected override async Task<HttpResponseMessage> SendAsync([NotNull] HttpRequestMessage request)
{
  .....
}

I also have a logging OnMethodBoundryAspect that is applied to all methods. The aspect just logs the method name and a time-stamp OnEntry, OnExit, and OnExeception, its very simple. The Aspect looks like so:

[ProvideAspectRole(StandardRoles.Tracing)]
[AspectRoleDependency(AspectDependencyAction.Order, AspectDependencyPosition.Before, StandardRoles.Validation)]
public sealed class MethodBoundaryLog : OnMethodBoundaryAspect
{
  public override void OnEntry(MethodExecutionArgs args)
  {
     // Writes to the logger
  }
  
  public override void OnException(MethodExecutionArgs args)
  {
    // Writes to the logger
  }
  
  public override void OnExit([NotNull] MethodExecutionArgs args)
  {
    // Writes to the logger
  }
  
}

I am using PostSharp 4.3.31 and when I try and build my application I get the following error:

Error LA0145: Incorrect ordering of advices on method 
"Demo.AllMessageHandler.SendAsync(System.Net.Http.HttpRequestMessage)". Advice "PostSharp.Patterns.Contracts.NotNullAttribute: Validates the value passed to parameter ''" cannot be after advice "Demo.Logging.MethodBoundaryLog: Transformed by state-machine-aware advice." because state-machine-aware advices must be positioned after all other advices. (43, 60)

This error doesn't occur on PostSharp 4.3.23 but I would like to be able to update my version to take advantage of the most recent features and bug fixes.

Any idea what's going on here and how I can resolve this?

Thank You.

  1. Support Staff 1 Posted by PostSharp Techn... on 12 Jul, 2017 02:26 PM

    PostSharp Technologies's Avatar

    Hello,

    In PostSharp 4.3.26 we have released a bug-fix related to the ordering of state-machine aware advices. It was always the case that state-machine advices must be ordered after non-state-machine advices. However, in some cases we didn't detect the incorrect ordering and didn't raise a build error. In the end the ordering was correct but not the one that was specified by the user. In your case MethodBoundaryLog was ordered after NotNull.

    Now we raise the build error when unsupported ordering is specified, so the user must fix it explicitly. Another complication in your case is that location validation aspects are not state-machine aware in the current version. I've filed an issue for us to reconsider this behavior (#15252).

    If you want MethodBoundaryLog to be ordered before NotNull, then you can implement a workaround using IAspectProvider and two aspects. OnEntry will be ordered before NotNull, OnExit and OnException will be ordered after NotNull.

    [PSerializable]
    [ProvideAspectRole( StandardRoles.Tracing )]
    [AspectRoleDependency( AspectDependencyAction.Order, AspectDependencyPosition.Before, StandardRoles.Validation )]
    public sealed class MethodBoundaryLog : OnMethodBoundaryAspect, IAspectProvider
    {
        public MethodBoundaryLog()
        {
            this.ApplyToStateMachine = false;
        }
    
        public override void OnEntry( MethodExecutionArgs args )
        {
            //
        }
    
        public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
        {
            yield return new AspectInstance(targetElement, new MethodBoundaryLogStateMachine());
        }
    
        [PSerializable]
        [ProvideAspectRole( StandardRoles.Tracing )]
        [AspectRoleDependency( AspectDependencyAction.Order, AspectDependencyPosition.After, StandardRoles.Validation )]
        public sealed class MethodBoundaryLogStateMachine : OnMethodBoundaryAspect
        {
            public MethodBoundaryLogStateMachine()
            {
                this.ApplyToStateMachine = true;
            }
            public override void OnException( MethodExecutionArgs args )
            {
                //
            }
    
            public override void OnExit( MethodExecutionArgs args )
            {
                //
            }
        }
    }
    

    -alex

  2. Support Staff 2 Posted by PostSharp Techn... on 21 Jul, 2017 12:42 PM

    PostSharp Technologies's Avatar

    Hello,

    We're closing the ticket for now as the bug has been internally filed as issue #15252. We will contact you as soon as the bug fix has been released.

    For more details on our support policies and prioritization of bug fixes, please visit https://www.postsharp.net/support/policies

    PostSharp Team

  3. PostSharp Technologies closed this discussion on 21 Jul, 2017 12:42 PM.

  4. ON re-opened this discussion on 03 Jan, 2018 10:57 PM

  5. 3 Posted by ON on 03 Jan, 2018 11:03 PM

    ON's Avatar

    Hi alex

    Thank you for suggesting the IAspectProvider. I am re-opening this ticket as I have finally gotten around to attempting to implement the MethodBoundaryLog in the manner you explained however I am still having issues with the state machine and ordering. I have an asynchronous unit test that I'm using to test the MethodBoundaryLog like so:

    [TestMethod]
    [MethodBoundaryLog]
    public async Task LogWriteAsyncTest()
    {
       // ...
    }
    

    When doing this I get the same error again:

    Error LA0145: Incorrect ordering of advices on method "LogWriteAsyncTest". Advice "MethodBoundaryLog: Wrapped by advice(s) OnEntry" cannot be after advice "MethodBoundaryLogStateMachine: Transformed by state-machine-aware advice." because state-machine-aware advices must be positioned after all other advices. (293, 27)

    Why is this occurring if MethodBoundaryLog is set to be ordered before Validation and MethodBoundaryLogStateMachine is ordered after Validation?

    On a similar note could you help me understand how the ordering of StandardRoles should be? I was not able to find anywhere that stated how ordering should be placed, should Validation be after Tracing or vice-versa? What about all of the other StandardRoles (i.e. Security, Caching, Persistence etc.)? What order do they need to be in. I'm a bit confused about this.

  6. Support Staff 4 Posted by PostSharp Techn... on 05 Jan, 2018 11:52 PM

    PostSharp Technologies's Avatar

    Hello,

    Your test method LogWriteAsyncTest doesn't have any Validation aspect applied to it, therefore the Validation role is ignored when ordering the aspects on the method. And this results in the unsupported ordering of the two logging aspects. I forgot to include the ordering of these two aspects in my original example.

    Please add another dependency attribute to the MethodBoundaryLogStateMachine class:

    [PSerializable]
    [ProvideAspectRole( StandardRoles.Tracing )]
    [AspectRoleDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, StandardRoles.Validation)]
    [AspectTypeDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, typeof(MethodBoundaryLog))]
    public sealed class MethodBoundaryLogStateMachine : OnMethodBoundaryAspect
    {
    

    Currently we do not have a documentation about the ordering of the StandardRoles. We will consider adding this information in one of the documentation updates. Please note, that we can document the ordering as it is implemented in PostSharp pattern libraries. When you develop your own custom aspects, it's mostly up to you how you want them to be ordered. The StandardRoles provide you with a convenient list of roles and allow you to order your custom aspects with PostSharp aspects.

    Below is the ordered list of some of the StandardRoles, as it is implemented in PostSharp 5.0. Please note that not all combinations of aspects are supported on all targets. We can change the order in the future PostSharp version.

    • Validation
    • Tracing
    • Threading
    • Caching

    -alex

  7. 5 Posted by ON on 10 Jan, 2018 01:56 AM

    ON's Avatar

    Thank you for the explanation Alex. I was able to figure out the AspectTypeDependency and get the aspect to work when there's no validation.

    Another issue I ran into is how to pass information from MethodBoundaryLog to MethodBoundaryLogStateMachine. In order to keep track of how long a method takes to enter and exit I save the DateTime in the OnEntry() method of the MethodBoundaryLog, how can I make it so that the entry time is available in MethodBoundaryLogStateMachine and is thread-safe?

  8. 6 Posted by ON on 12 Jan, 2018 01:25 AM

    ON's Avatar

    Hi Alex,

    Do you have any suggestions for my issue? I know about MethodExectutionTag but as I understand it it's for passing data between the same aspect. How can I pass data between different aspects and have it be thread-safe? If that's not possible do you know of another way I can get the method's entry time into the aspect that has the OnExit and OnException methods?

    Thanks.

  9. Support Staff 7 Posted by PostSharp Techn... on 12 Jan, 2018 12:44 PM

    PostSharp Technologies's Avatar

    Hello,

    I'm sorry for the delay with the response. In general case I would recommend using the CallContext class when you need to share data between different aspects.

    However, I'd take a different approach in case of your logging/profiling aspect. First of all, you need to decide what to include in the execution time of an async method. Normally, you don't need to measure the execution of the compiler-generated kick-off method. Also, to measure only the real work performed by the method and exclude the time spent awaiting, you need to pause the timer in the OnYield handler and resume it in the OnResume handler.

    In our example the MethodBoundaryLog aspect is applied to the state-machine kick-off method, and the MethodBoundaryLogStateMachine aspect is applied to the MoveNext method inside state-machine class where all the actual work is done. My suggestion is to implement time-tracking in the MethodBoundaryLogStateMachine class only and use MethodExectutionTag to share the data. You would need to additionally implement the OnEntry, OnYield, OnResume methods. You can refer to our sample ProfileAttribute: http://samples.postsharp.net/f/PostSharp.Samples.Profiling/ProfileA...

    Please let us know if you need more help with implementation.

    -alex

  10. 8 Posted by ON on 18 Jan, 2018 09:11 PM

    ON's Avatar

    Thank You Alex for all of your help on this issue. I implemented the OnEntry int the MethodBoundaryLogStateMachine aspect along with the OnExit and OnException and that seems to work now.

    I did have a couple of questions related to this that I observed while I was testing this functionality.

    • I'm doing a check in the MethodBoundaryLogStateMachine constructor that checks if MethodBoundaryLog has been applied in order to avoid the scenario where MethodBoundaryLogStateMachine is applied directly instead of created via ProvideAspect() in MethodBoundaryLog. I'm using the following to see if MethodBoundaryLog has been applied:
    public MethodBoundaryLogStateMachine(object targetElement)
    {
             if (PostSharpEnvironment.IsPostSharpRunning)
             {
                    var aspectRepositoryService = PostSharpEnvironment.CurrentProject.GetService<IAspectRepositoryService>();
                    IAspectInstance[] aspectInstances = aspectRepositoryService.GetAspectInstances(targetElement);
                    IEnumerable<IAspectInstance> mblInstances = aspectInstances.Where(mblInstance => mblInstance != null && mblInstance.AspectType == typeof(MethodBoundaryLog)).ToArray();
                     
                      if(!mblInstances.Any())
                       {
                             throw new Exception();
                        }
             }
    }
    

    My question is..... how can I unit test this? All of this logic takes place at compile time but unit tests are at runtime so I can't seem to find a way to unit test this logic. Any suggestions?

    • The other thing that I observed is that when I apply MethodBoundaryLog directly to an async method I get the following build warnings:

    Warning LA0134: The aspect MethodBoundaryLog has been applied to the state-machine method "LogWriteWriteAsync", but the ApplyToStateMachine property has not been set. The aspect may behave in an unintuitive way unless you set the ApplyToStateMachine property to true. (300, 27)

    It seems like it does not take into account the MethodBoundaryLogStateMachine aspect which does have ApplyToStateMachine property set to true. How can I fix this warning so it does not appear?

    Warning LA0156: Missing code saving information on aspect type "MethodBoundaryLog". Code saving metrics will be inaccurate. Add [LinesOfCodeAvoided] to the aspect class or specify the LinesOfCodeAvoided property of the advice custom attribute or ignore the warning LA0156. (1, 1)

    Does MethodBoundaryLog not save lines of code since MethodBoundaryLogStateMachine is doing most of the work? Should I set the [LinesOfCodeAvoided] value to 0?

    Thanks Again.

Reply to this discussion

Internal reply

Formatting help / Preview (switch to plain text) No formatting (switch to Markdown)

Attaching KB article:

»

Attached Files

You can attach files up to 10MB

If you don't have an account yet, we need to confirm you're human and not a machine trying to post spam.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac