| Severity | Error |
| Reported by | PostSharp |
| Message | Cannot apply the instance-scoped aspect "<Aspect>" to "<Target>" because this declaration is in module "<Module>", not in the module being processed. Apply the aspect to a declaration of the current module instead, for example to an override of the method. |
Cause
An instance-scoped aspect, which implements IInstanceScopedAspect, has one aspect instance for each instance of the target class, created when the class is instantiated. PostSharp adds the code that creates and stores these aspect instances to the class that declares the target. It can only modify the assembly that it is processing, so it cannot apply an instance-scoped aspect to a member declared in another assembly, <Module>.
A custom attribute cannot be placed on a member of another assembly, so the target usually comes from an IAspectProvider: reflection methods such as Type.GetMethod also return the members that a class inherits, including those of a base class defined in a library.
In the following example, the [TrackCommand] aspect provider gives an instance-scoped [CountExecutions] aspect to the Execute method of SaveCommand. SaveCommand does not override Execute, so GetMethod returns the method of CommandBase, which is defined in a library.
// @DisabledMessages(PS0131)
using System;
namespace Errors.LA0163_Error.Commands;
public abstract class CommandBase
{
public virtual void Execute() => Console.WriteLine( $"Executing {this.GetType().Name}." );
}
using System;
using System.Collections.Generic;
using System.Reflection;
using Errors.LA0163_Error.Commands;
using PostSharp.Aspects;
using PostSharp.Serialization;
namespace Errors.LA0163_Error;
// An instance-scoped aspect: each command object gets its own aspect instance, which counts its executions.
[PSerializable]
public class CountExecutionsAttribute : MethodInterceptionAspect, IInstanceScopedAspect
{
[PNonSerialized]
private int _count;
public object CreateInstance( AdviceArgs adviceArgs ) => this.MemberwiseClone();
public void RuntimeInitializeInstance()
{
}
public override void OnInvoke( MethodInterceptionArgs args )
{
this._count++;
Console.WriteLine( $"Execution {this._count} of {args.Instance!.GetType().Name}." );
args.Proceed();
}
}
[PSerializable]
public class TrackCommandAttribute : TypeLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
{
MethodInfo execute = ((Type) targetElement).GetMethod( nameof(CommandBase.Execute) )!;
yield return new AspectInstance( execute, new CountExecutionsAttribute() );
}
}
// SaveCommand inherits Execute from CommandBase, which is defined in another assembly.
[TrackCommand]
public class SaveCommand : CommandBase
{
}
public static class Program
{
public static void Main()
{
SaveCommand command = new();
command.Execute();
command.Execute();
}
}
PostSharp reports the error on the declaration of the target member, in the source of the library:
error LA0163: LA0163_Error.Dependency.cs(9,25): Cannot apply the instance-scoped aspect "Errors.LA0163_Error.CountExecutionsAttribute" to "Errors.LA0163_Error.Commands.CommandBase.Execute" because this declaration is in module "LA0163_Error.Dependency.dll", not in the module being processed. Apply the aspect to a declaration of the current module instead, for example to an override of the method.
Resolution
- Override the member in a class of the current project, and apply the aspect to the override. A reflection method such as
Type.GetMethodthen returns the override. - In an aspect provider, skip the members of other assemblies, for example by comparing the
Moduleof the member with theModuleof the target type, or by passingBindingFlags.DeclaredOnlyto the reflection method. - If the aspect does not need state for each instance, do not implement IInstanceScopedAspect. PostSharp can then apply some aspects, such as a MethodInterceptionAspect, to a method of another assembly by modifying the calls to the method in the current assembly. For a virtual method, PostSharp warns that this is not fully supported (LA0183).
In the following example, SaveCommand overrides Execute, so each command counts its own executions.
using System;
using System.Collections.Generic;
using System.Reflection;
using Errors.LA0163_Fixed.Commands;
using PostSharp.Aspects;
using PostSharp.Serialization;
namespace Errors.LA0163_Fixed;
// An instance-scoped aspect: each command object gets its own aspect instance, which counts its executions.
[PSerializable]
public class CountExecutionsAttribute : MethodInterceptionAspect, IInstanceScopedAspect
{
[PNonSerialized]
private int _count;
public object CreateInstance( AdviceArgs adviceArgs ) => this.MemberwiseClone();
public void RuntimeInitializeInstance()
{
}
public override void OnInvoke( MethodInterceptionArgs args )
{
this._count++;
Console.WriteLine( $"Execution {this._count} of {args.Instance!.GetType().Name}." );
args.Proceed();
}
}
[PSerializable]
public class TrackCommandAttribute : TypeLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
{
MethodInfo execute = ((Type) targetElement).GetMethod( nameof(CommandBase.Execute) )!;
yield return new AspectInstance( execute, new CountExecutionsAttribute() );
}
}
[TrackCommand]
public class SaveCommand : CommandBase
{
// SaveCommand overrides Execute, so the aspect is applied to a method of the current project.
public override void Execute() => base.Execute();
}
public static class Program
{
public static void Main()
{
SaveCommand command = new();
command.Execute();
command.Execute();
}
}
Execution 1 of SaveCommand.
Executing SaveCommand.
Execution 2 of SaveCommand.
Executing SaveCommand.
See Also
Reference
IInstanceScopedAspect
IAspectProvider
Other Resources
Understanding Aspect Lifetime and Scope
Adding Aspects Programmatically Using IAspectProvider