Dependency injection on the loose: Automated injection of method parameters
Yes, you've read it right: in this post I will show you how to inject arguments to method calls and create something like a 'method call' scope. It's not perfect and there are a lot of things that still need to be considered more carefully, but I was really excited about this, so here it goes :)
So let's say you have a resource that you want to use: something like a unit-of-work (i.e. EF DbContext). It handles unmanaged resources and it has its own state, so you make it IDisposable. And then, whenever you use it in a method, you do something like this:
public class Service
{
public void Method1()
{
using (var uow=new UnitOfWork())
{
// method logic goes here
}
}
public void Method2()
{
using (var uow=new UnitOfWork())
{
// method logic goes here
}
}
}
You see the pattern right? When a method starts, you create a unit-of-work and wrap it into a using block. Quite tiresome, but you have to do this: you cannot put the uow into a private field in the class and use constructor injection, because the uow needs to be short lived and disposed as soon as possible. There are exceptions of course; for example if you do web development, you can bind the unit-of-work to an http request and then it is disposed when the request ends. But if you do client side development (God-forbid, a thick client), how do you scope the unit-of-work? There is no 'unit of work', like a request. You have pages, views, you have navigation, but a lot of time can pass between navigations with doing nothing on a page. So you're stuck with the manual instantiation, using using (pardon the pun, I've been waiting for this since the beginning of the post). Of couse, you can use factories or inject the container into the Service class and resolve from that, but still, you have to manage the whole thing yourself.
Here's my idea: what if some arguments of a method could be resolved dynamically, without specifying the exact object when the method is actually called? What is the method call itself could be wrapped into it's own scope?
So let's dig in :) First, let's create some code that can be used to test the solution, using Autofac. Something like this:
public interface IUnitOfWork : IDisposable // I know, but let's have this here for the sake of the example
{
void Commit();
}
public class UnitOfWork : IUnitOfWork
{
public void Commit()
{
Console.WriteLine("Committing...");
}
public void Dispose() {}
}
public interface IService
{
void DoSomething(IUnitOfWork uow);
}
public class Service : IService
{
public void DoSomething(IUnitOfWork uow)
{
if (uow == null)
{
Console.WriteLine("Null");
}
else
{
Console.WriteLine("Not null");
uow.Commit();
}
}
}
public class ViewModel
{
private readonly IService service;
public ViewModel(IService service)
{
this.service = service;
}
public void DoSomethingElse()
{
service.DoSomething();
}
}
var builder = new ContainerBuilder();
builder.RegisterType<ViewModel>();
builder.RegisterType<Service>().As<IService>();
builder.RegisterType<UnitOfWork>()
.As<IUnitOfWork()>
.InstancePerLifetimeScope();
var container = builder.Build();
var vm = container.Resolve<ViewModel>();
vm.DoSomethingElse();
So we have a couple of types here. We have a ViewModel, which is just that: a viewmodel that you'd use in a WPF or UWP application. The ViewModel relies on the BLL to handle business transactions, in this case the Service class. The Service class relies on the UnitOfWork class for data access for example.
The ViewModel is resolved through the container and through that it receives and instance of Service. That's nothing new (again, pardon the pun), we use simple constructor injection.
Now this code doesn't compile, because we have to specify the argument for the Service.DoSomething() method. And that's what it's all about: I do not want to do that. That's a dependency, and I want it resolved through dependency injection. And I want every call of that method to form its own lifetime scope. I want something like this to happen:
public class ViewModel
{
private readonly IService service;
public ViewModel(IService service)
{
this.service = service;
}
public void DoSomethingElse()
{
using (var scope = container.BeginLifetimeScope()) // never mind the fact that we don't have a reference to the container here - yet
{
service.DoSomething(scope.Resolve<IUnitOfWork>());
}
}
}
Every call of that method should be wrapped in its own lifetime scope, and arguments should be resolved from that scope and passed to the method. But without having to actually code this inside the methods, or outside the methods. I could, of course just wrap them with an Action or something, but that's clumsy: it creates weird stack traces, it creates closures, it's hard to read, to debug (sometimes state
Dynamic proxies and default parameters
And that's where dynamic proxies come in. You might know the Castle.DynamicProxy library. It is a tool that's used by a lot of other tools to perform 'magic': Moq uses it to create the mocks from the configuration, Autofac uses it for the aggregate services feature, NHibernate uses it to enable lazy-loading. All of these have one thing in common: you need to create a proxy type for the original and add some functionality to it (just like a real proxy). And that's what we'll be using to generate a proxy for the Service class, so the proxy creates the lifetime scope and resolves the arguments. But first, let's modify code so that the one above actually compiles:
public interface IService
{
void DoSomething(IUnitOfWork uow=default);
}
public class Service : IService
{
public virtual void DoSomething(IUnitOfWork uow=default)
{
if (uow == null)
{
Console.WriteLine("Null");
}
else
{
Console.WriteLine("Not null");
uow.Commit();
}
}
}
Note the default vlue for the unit-of-work parameter. Now the code compiles even if you don't specify a value, because null is subsituted at the call site (also note the C# 7.1 feature allowing you to write just 'default', without the type name — sweet). Also note how I changed the DoSomething() method to virtual. That's very important for the proxy part.
Now, install the Castle.Core nuget package. This allows you to create the magical runtime proxies. The concept is quite simple, actually:
1 First, you create the so called interceptors These are, again, just what their name suggests: interceptors for your method calls, that allow you to add logic before and after the method call (basically, they are special decorators).
2 Use the one line of code that Castle.Core gives you to create the proxy and specify the interceptor. What this basically does is that it creates a subclass of your original type, overrides the virtual methods, and calls the base version in the descendant — but before doing so, it calls the interceptors that you can specify.
OK, let's go step by step.
Creating an attribute for injectable parameters
To show which parameters are to be injected, let's create an attribute that parameters can be annotated with:
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class ResolvedAttribute : Attribute { }
That's it (don't forget to seal your attributes).
Set up methods and parameters for injection
So again, what we need to do for method parameters to be injectable:
- The method must be virtual. This is to allow for the dynamic proxy feature to override the method in a descendant class.
- The parameter has to be optional. This is to allow omitting the parameter at the method call site.
- And of course, we didn't create the attribute for nothing. The parameter has to be annotated with the attribute (I like to do it at the interface and at the implementation too):
public interface IService
{
void DoSomething([Resolved] IUnitOfWork uow = default);
}
public class Service : IService
{
public virtual void DoSomething([Resolved] IUnitOfWork uow = default)
{
if (uow == null)
{
Console.WriteLine("Null");
}
else
{
Console.WriteLine("Nem null");
uow.Commit();
}
}
}
Creating the interceptor
To create the interceptor, all you have to do is implement an interface from Castle.Core
public class ResolveParameterInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
}
}
That's your most basic intereptor, the 'emptyceptor' (I know, but trust me, it's almost over). The invocation object represents the method call, calling Proceed allows the method call to continue. But before allowing the invocation to proceed, we can do a lot of things. For example, create a new lifetime scope, wrap it in a using block, check the method parameters for the attribute we created, and if they have that, pass in an object resolved from the lifetime scope.
public class ResolveParameterInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
using (var scope = CreateScope())
{
var resolveParameters =
invocation.Method.GetParameters()
.Where(p => p.GetCustomAttribute<ResolvedAttribute>() != null);
foreach (var p in resolveParameters)
{
invocation.SetArgumentValue(p.Position, scope.Resolve(p.ParameterType));
}
invocation.Proceed();
}
}
}
We are almost there. The only question is how to create the liftime scope around the invocation. In my experience, if the question 'how do I do that' comes up in a class, this means that the class cannot do it, because it is not its responsibility yet. And, following the Single Responsibility principle, it would be wise not to introduce this new responsiblity, but instead factor it out to a new interface, and then injet that into the interceptor.
public interface ILifetimeScopeFactory
{
ILifetimeScope BeginLifetimeScope();
}
public class ResolveParameterInterceptor : IInterceptor
{
private readonly ILifetimeScopeFactory scopeFactory;
public ResolveParameterInterceptor (ILifeTimescopeFactory factory)
{
this.scopeFactory=factory;
}
public void Intercept(IInvocation invocation)
{
using (var scope = scopeFactory.BeginLifetimeScope())
{
var resolveParameters =
invocation.Method.GetParameters()
.Where(p => p.GetCustomAttribute<ResolvedAttribute>() != null);
foreach (var p in resolveParameters)
{
invocation.SetArgumentValue(p.Position, scope.Resolve(p.ParameterType));
}
invocation.Proceed();
}
}
}
We'll worry about implementing the scope-factory later (that's a nice aspect of DI; you can worry about things later).
Wire everything together
So know, we have the components, we just have to wire them together. Autofac has extra support for dynamic proxies. You can create registrations that say 'I want to use X for type Y, but for X, enable proxy generation and use the generated proxy with interceptors Z and W'. To do this, you need to install the Autofac.Extras.DynamicProxy NuGet package. And then, you can register the Service type like this:
builder.RegisterType<ResolveParameterInterceptor>();
builder.RegisterType<Service>()
.EnableClassInterceptors()
.InterceptedBy(typeof(ResolveParameterInterceptor))
.As<IService>();
The first line just registers the interceptor into the container. The second registration says basically to:
- register Service into the container
- for Service, enable the dynamic proxy generation interceptor thingie
- use the ResolveParameterInterceptor when intercepting
- and Service should be resolved whenever something asks for an IService
And that's it. We're almost done. The last thing we need is to create a scope factory implementation, and register that to the factory too. I could leave that up to you, but since I've already done it, here it goes.
Creating the scope factory
For the scope factory implementation, I used the service locator of the project.
I know, service locator is bad. Or at least, that's what we are tought to believe :) I simply say that I'm against service locator most of the time. But not always. When writing integration code (that is code that helps us code more efficiently) and not business logic (that is code that you'd actually unit test, derive from the specification and get money for), it can be a valid approach.
So back to the service-locator. First, I added the CommonServiceLocator Nuget package to the project (formerly nown and Microsoft.Practices.ServiceLocation). And then, I created my own Autofac-based implementation based on the Autofac.Extras.CommonServiceLocator NuGet package. I did that for two reasons:
- I needed to change the inner workings of the original implementation to use the ILifetimeScope instead of the IComponentContext (I mean, we always use the root container anyway, which is both)
- Microsoft renamed the dll a couple of days ago, which seems to have broken the Autofac.Extras.CommonServiceLocator dll, so it was easier to include that one class here (note the line for contribution: be nice, people).
// https://github.com/conwid/Autofac.Extras.CommonServiceLocator
public class AutofacServiceLocator : ServiceLocatorImplBase
{
private readonly ILifetimeScope _container;
public ILifetimeScope Container => _container;
public AutofacServiceLocator(ILifetimeScope container)
{
_container = container ??
throw new ArgumentNullException("container");
}
protected override object DoGetInstance(Type serviceType, string key)
{
if (serviceType == null)
{
throw new ArgumentNullException(nameof(serviceType));
}
return key != null ?
_container.ResolveNamed(key, serviceType) :
_container.Resolve(serviceType);
}
protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
{
if (serviceType == null)
{
throw new ArgumentNullException(nameof(serviceType));
}
var enumerableType = typeof(IEnumerable<>)
.MakeGenericType(serviceType);
object instance = _container.Resolve(enumerableType);
return ((IEnumerable)instance).Cast<object>();
}
}
Then, wire everything together with the servicelocator:
var container = builder.Build();
ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));
And then, implement the factory:
public class AutofacServiceLocatorLifetimeScopeFactory : ILifetimeScopeFactory
{
public ILifetimeScope BeginLifetimeScope()
{
return ((AutofacServiceLocator)ServiceLocator.Current)
.Container.BeginLifetimeScope();
}
}
Finaly, register the factory to the container, befire calling .Build() and assigning the built container to the servicelocator. The registrations look like this:
var builder = new ContainerBuilder();
builder.RegisterType<ViewModel>().SingleInstance();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<AutofacServiceLocatorLifetimeScopeFactory >().As<ILifetimeScopeFactory>().SingleInstance();
builder.RegisterType<ResolveParameterInterceptor>();
builder.RegisterType<Service>().EnableClassInterceptors().InterceptedBy(typeof(ResolveParameterInterceptor)).As<IService>();
var container = builder.Build();
ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));
var vm = container.Resolve<ViewModel>();
vm.DoSomethingElse();
And if you've hung around long enough to read this, you're reward is here: lo' and behold, a unit-of-work is injected into the method. You can test it and everything :)
The last thing that you might do is create a nice extension method to hide that very complicated registration of setting up the interception process. I did just that in a this Github repository, where you can download the code from and play with it a little bit. You can create a ctor for the UnitOfWork and put a breakpoint in it and do the same for the Dispose() method. Then do multiple calls to the vm.DoSomething() and you'll see that they are really two different instances and they really are disposed. Have fun :)