I have designed couple of aspects using PostSharp for different projects, but there is a design flaw in many of them: dependency management.
This is a question about injection of dependencies into aspects, not about dependency injection using aspects
Due to nature of PostSharp post-compilation there are several limitations applied to aspects
- Constructor injection is not applicable. Constructor will be executed once during post-compilation, then object will be serialized and later deserialized multiple times at runtime. There is a method for runtime initialization
IEventLevelAspect.RuntimeInitialize
. - Neither property injection is possible to do : properties will be initialized before runtime, and IL weaver will rewrite code, so decoration with some kind of
[Injection]
attribute will be not possible also - Neither Ambient context nor Service Locator are not suitable because they will break unit testing isolation
It's only possible to provide values of value types known during compilation and
System.Type
next to aspect decoration:[MyAspect(integerValue = 1, providedType = typeof(Type))]
After all those limitations, there is only one approach (known to me): provide System.Type
of abstract factory or class with factory method, so aspect could creates its instance during runtime initialization and resolves dependency using it.
PostSharp aspect that has dependency injection
Here is an example of trivial tracing aspect, that has dependency injection
[Serializable]
public class TracingAspectAttribute : OnMethodBoundaryAspect
{
// Question #1: Is there any better way to design aspect
// and to inject dependency into it?
public Type AbstractFactoryType { get; set; }
private ILogger Logger { get; set; }
// Compile time validation.
// Question #2: Better approach to ensure during post-compile time
// that abstract factory could be created at runtime using Activator.CreateInstance
public override bool CompileTimeValidate(MethodBase method)
{
var result = true;
var methodInfo = method as MethodInfo;
// check that AbstractFactoryType contains proper System.Type
if (AbstractFactoryType == null) // check that AbstractFactoryType is provided
{
// break build with following message
Message.Write(methodInfo, SeverityType.Error, "999", "AbstractFactoryType is null");
result = false;
}
else if (!(typeof(IAbstractFactory<ILogger>).IsAssignableFrom(AbstractFactoryType))) // check that AbstractFactoryType is proper type
{
// break build with following message
Message.Write(methodInfo, SeverityType.Error, "999", "Only abstract facory derived from IAbstractFactory<ILogger> allowed");
result = false;
}
else if (AbstractFactoryType.IsAbstract || AbstractFactoryType.IsInterface) // check that instance of AbstractFactoryType could be created
{
// break build with following message
Message.Write(methodInfo, SeverityType.Error, "999", "Only concrete facory derived from IAbstractFactory<ILogger> allowed");
result = false;
}
return result;
}
// Initializes the current aspect at runtime
public override void RuntimeInitialize(MethodBase method)
{
// create instance of Abstract Factory
var abstractFactory = Activator.CreateInstance(AbstractFactoryType);
// create an instance of dependency, only instances of IAbstractFactory<ILogger> could be here
// proven by CompileTimeValidate method
Logger = ((IAbstractFactory<ILogger>)abstractFactory).CreateInstance();
}
// Method executed before the body of methods to which this aspect is applied.
public override void OnEntry(MethodExecutionArgs args)
{
Logger.Log(string.Format("OnEntry {0}.{1}(...)", args.Method.DeclaringType.FullName, args.Method.Name));
}
// Method executed after the body of methods to which this aspect is applied
public override void OnExit(MethodExecutionArgs args)
{
Logger.Log(string.Format("OnExit {0}.{1}(...)", args.Method.DeclaringType.FullName, args.Method.Name));
}
}
Sample code is available at gist and full example is at github
Example of usage
[TracingAspect(AbstractFactoryType = typeof(FakeLoggerFactory))]
public class FakeService
{
public int Process(int n, int m)
{
return n * m;
}
}
More samples could be found at github
My questions are following
- Is there any better way to design aspect and to inject dependency into it?
- Better approach to ensure during post-compile time that abstract factory could be created at runtime using
Activator.CreateInstance
Comment from SharpCrafter support
The best way is to use a global singleton service locator, called from method RuntimeInitialize.
If you need the ability to change the DI container dynamically (for testing, presumably), you have to write some C# code to handle this concern. For instance, you may maintain a static collection of all aspects in your AppDomain and set the dependencies from the test.
Note that most aspects have static lifetime, so it does not fit well with the dependency container framework.
Postsharp Atributes and Dependency Injection @ SharpCrafters support