13
\$\begingroup\$

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

  1. 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.
  2. 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
  3. Neither Ambient context nor Service Locator are not suitable because they will break unit testing isolation
  4. 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

  1. Is there any better way to design aspect and to inject dependency into it?
  2. 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

\$\endgroup\$
1

1 Answer 1

9
+100
\$\begingroup\$

I've not used PostSharp in a number of years, but have often used the following pattern to get around life-cycle issues with DI on attributes. It's a little manual, so should be seen as the exception rather than the rule, but it's easy to follow.

In your aspect, define a static factory method specifically designed to create your dependency . Define it as a Func and during execution of the aspect, call that func to get an instance of your dependency in the current context.

For example:

public class SomeDependency
{
    public void DoSomething()
    {
    }
}

public class ContainerRegistrationCode
{
    public void RegisterTypesInContainer()
    {
        SomeAspect.CreateSomeDependency = () => new SomeDependency(); 

        // Probably something like
        // SomeAspect.GetDependency = () => Kernel.GetService<SomeDependency>();
        // goes here
    }
}

public class SomeAspect
{
    public static Func<SomeDependency> CreateSomeDependency { get; set; }

    public void OnExecute()
    {
        //Some behavior
        var dependencyINeed = CreateSomeDependency();
        dependencyINeed.DoSomething();
    }
}

This allows you to declaratively bind up your dependencies (unfortunately manually, though I'm sure you could work out some convention for auto-binding), and ensures that your container gives you the correct object on each execution.

I'm not sure if PostSharps IL weaving would interfere with this, but it's simple, clear and effective. We initially started using it to control the life-cycle of injected components that required access to NHibernate ISessions in ASP.NET MVC attributes.

If you intend to wireup many of these types of dependencies, you could likely just use reflection to find any properties on your attributes following a naming convention, and bind them to your container.

This obviously works fine for testability, as you just wire up the Func on your test setup. Obviously, buyer beware if you're running your tests concurrently because of the static.

\$\endgroup\$
5
  • \$\begingroup\$ Thx David, I'm going to try this idea with PostSharp to unveil questions regarding aspect's lifetime and IL weaver. Anyway this approach contains design flaw: as long as CreateSomeDependency is static, it will add unnecessary complexity while testing of aspect. \$\endgroup\$
    – Akim
    Jan 13, 2013 at 14:46
  • \$\begingroup\$ I gave a try to this approach in both with static and instance scope of PostSharp aspect. It works fine, but ` public static Func<SomeDependency> CreateSomeDependency { get; set; }` makes it hard to unittest in parallel. Thx anyway. Here is a code snippet \$\endgroup\$
    – Akim
    Jan 22, 2013 at 12:12
  • \$\begingroup\$ I'd suggest if it's an acceptable solution apart from that, that you serialize the tests around your aspects. I agree that it's not ideal for this reason, but it's not too different than any unit test that requires a [SetUp] ending up a the lesser of the evils ;) \$\endgroup\$ Jan 23, 2013 at 14:39
  • \$\begingroup\$ Same issue will appear at multi-threaded environment, when different dependencies will be injected into different instances of aspect \$\endgroup\$
    – Akim
    Jan 23, 2013 at 15:03
  • \$\begingroup\$ That depends on your container configuration. It's also why we actually call the Func during OnExecute rather than calling it and caching the output. If you use your DI container to scope your injected dependencies to a suitable unit of work, you'll always get the "currently scoped" instance during execution. This works well in a very high transaction website for injecting ISessions, which are request scoped as every time the attribute executes, it calls the container to ask for the current instance. \$\endgroup\$ Jan 24, 2013 at 20:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.