Open sandboxFocusImprove this doc

LA0184: Advice Applied to a Virtual Method of Another Assembly

Severity Warning
Reported by PostSharp
Message Applying the advice "<Advice>" to virtual external method "<Method>" may lead to unexpected behavior. Applying OnMethodEntryAdvice, OnMethodSuccessAdvice, OnMethodExitAdvice, OnMethodExceptionAdvice, and OnMethodInvokeAdvice on external virtual methods is not fully supported.

Cause

When you multicast an aspect to a method of another assembly with AttributeTargetAssemblies, PostSharp cannot modify the method itself. Instead, it advises the calls that the current project makes to the method. When the method is virtual, such a call can reach an override, but the advice still runs for the method of the base type: the advice describes a method that is not the one that executes, and an override that is itself advised can run the advice twice.

PostSharp reports LA0184 for each advice of an aspect built from advices, OnMethodEntryAdvice, OnMethodSuccessAdvice, OnMethodExitAdvice, OnMethodExceptionAdvice or OnMethodInvokeAdvice, that is applied to a virtual or abstract method of another assembly. The warning is reported on the advice method of the aspect, <Advice>. Aspects derived from OnMethodBoundaryAspect, OnExceptionAspect or MethodInterceptionAspect report LA0183 in the same situation.

In the following example, a library defines a MessageSender class with a virtual Send method, and an EmailSender class that overrides it:

using System;

namespace Errors.LA0184_Error.Messaging;

public class MessageSender
{
    public virtual void Send( string message ) => Console.WriteLine( $"Sending: {message}" );
}

public class EmailSender : MessageSender
{
    public override void Send( string message ) => Console.WriteLine( $"Emailing: {message}" );
}

The application applies the [Log] aspect to MessageSender.Send and calls it on an EmailSender:

// @SkipPostSharpOnDependency

using System;
using Errors.LA0184_Error.Messaging;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

// The aspect is applied to the virtual method MessageSender.Send of the referenced library.
[assembly: Errors.LA0184_Error.Log(
    AttributeTargetAssemblies = "*Dependency",
    AttributeTargetTypes = "Errors.LA0184_Error.Messaging.MessageSender",
    AttributeTargetMembers = "Send" )]

namespace Errors.LA0184_Error;

[PSerializable]
public class LogAttribute : MethodLevelAspect
{
    // PostSharp reports the warning on the advice that is applied to the virtual method of the library.
    [OnMethodEntryAdvice]
    public void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"Calling {args.Method.DeclaringType!.Name}.{args.Method.Name}" );
    }
}

public class NotificationService
{
    private readonly MessageSender sender = new EmailSender();

    public void Notify( string message ) => this.sender.Send( message );
}

public static class Program
{
    public static void Main()
    {
        new NotificationService().Notify( "Your order has shipped." );
    }
}
warning LA0184: LA0184_Error.cs(22,17): Applying the advice "Errors.LA0184_Error.LogAttribute.OnEntry(PostSharp.Aspects.MethodExecutionArgs)" to virtual external method "Errors.LA0184_Error.Messaging.MessageSender.Send(System.String)" may lead to unexpected behavior. Applying OnMethodEntryAdvice, OnMethodSuccessAdvice, OnMethodExitAdvice, OnMethodExceptionAdvice, and OnMethodInvokeAdvice on external virtual methods is not fully supported.

The program runs, but the advice reports a call to MessageSender.Send although EmailSender.Send executes:

Calling MessageSender.Send
Emailing: Your order has shipped.

Resolution

  • Apply the aspect to the methods of the current project that call the external method, rather than to the external method itself.
  • When you own the library, apply the aspect in the library, so that PostSharp modifies the virtual method and its overrides directly.
  • Exclude the virtual methods from the multicast with AttributeTargetMemberAttributes set to NonVirtual.

In the following example, the aspect is applied to NotificationService.Notify, which calls MessageSender.Send.

// @SkipPostSharpOnDependency

using System;
using Errors.LA0184_Fixed.Messaging;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0184_Fixed;

[PSerializable]
public class LogAttribute : MethodLevelAspect
{
    [OnMethodEntryAdvice]
    public void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"Calling {args.Method.DeclaringType!.Name}.{args.Method.Name}" );
    }
}

public class NotificationService
{
    private readonly MessageSender sender = new EmailSender();

    // The aspect is applied to a method of the current project, which calls the virtual method of the library.
    [Log]
    public void Notify( string message ) => this.sender.Send( message );
}

public static class Program
{
    public static void Main()
    {
        new NotificationService().Notify( "Your order has shipped." );
    }
}
Calling NotificationService.Notify
Emailing: Your order has shipped.

See Also

Reference

AttributeTargetAssemblies
OnMethodEntryAdvice
OnMethodInvokeAdvice

Other Resources

Understanding Attribute Multicasting