Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple Injector: Inject same UnitOfWork instance across services of the same graph

I have multiple services, each of which have a UnitOfWork injected into the constructor using the Simple Injector IoC container.

Currently I can see each UnitOfWork instance is a separate object, this is bad as i am using Entity Framework and require the same context reference across all units of work.

How can I ensure the same UnitOfWork instance is injected into all services per each resolve request? My UnitOfWor will be saved by an external command handler decorator after the command completes.

Please note, this is a common library and will be used for both MVC and Windows Forms, it would be nice to have a generic solution for both platforms if possible.

Code is below:

// snippet of code that registers types
void RegisterTypes()
{
    // register general unit of work class for use by majority of service layers
    container.Register<IUnitOfWork, UnitOfWork>();

    // provide a factory for singleton classes to create their own units of work 
    // at will
    container.RegisterSingle<IUnitOfWorkFactory, UnitOfWorkFactory>();

    // register logger
    container.RegisterSingle<ILogger, NLogForUnitOfWork>();

    // register all generic command handlers
    container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>),
        AppDomain.CurrentDomain.GetAssemblies());

    container.RegisterDecorator(typeof(ICommandHandler<>),
        typeof(TransactionCommandHandlerDecorator<>));

    // register services that will be used by command handlers
    container.Register<ISynchronisationService, SynchronisationService>();
    container.Register<IPluginManagerService, PluginManagerService>();
}

The desired outcome of the below line is to create a object which has a shared UnitOfWork instance throughout the constructed object graph:

var handler = Resolve<ICommandHandler<SyncExternalDataCommand>>();

Here are my services:

public class PluginManagerService : IPluginSettingsService
{
    public PluginManagerService(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
    }

    private readonly unitOfWork;

    void IPluginSettingsService.RegisterPlugins()
    {
       // manipulate the unit of work
    }
}

public class SynchronisationService : ISynchronisationService
{
    public PluginManagerService(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
    }

    private readonly unitOfWork;

    void ISynchronisationService.SyncData()
    {
       // manipulate the unit of work
    }
}

public class SyncExternalDataCommandHandler
    : ICommandHandler<SyncExternalDataCommand>
{
    ILogger logger;
    ISynchronisationService synchronisationService;
    IPluginManagerService pluginManagerService;

    public SyncExternalDataCommandHandler(
        ISynchronisationService synchronisationService, 
        IPluginManagerService pluginManagerService, 
        ILogger logger)
    {
        this.synchronisationService = synchronisationService;
        this.pluginManagerService = pluginManagerService;
        this.logger = logger;
    }

    public void Handle(SyncExternalDataCommand command)
    {
        // here i will call both services functions, however as of now each
        // has a different UnitOfWork reference internally, we need them to 
        // be common.
        this.synchronisationService.SyncData();
        this.pluginManagerService.RegisterPlugins();
    }
}
like image 262
morleyc Avatar asked Jun 08 '12 19:06

morleyc


1 Answers

Which registration you need depends on the type of application. Since you are talking about two different frameworks (MVC and WinForms), both will have a different registration.

For an MVC application (or web applications in general), the most common thing to do is to register the unit of work on a per web request basis. For instance, the following registration will cache the unit of work during a single web request:

container.Register<IUnitOfWork>(() =>
{
    var items = HttpContext.Current.Items;

    var uow = (IUnitOfWork)items["UnitOfWork"];

    if (uow == null)
    {
        items["UnitOfWork"] = uow = container.GetInstance<UnitOfWork>();
    }

    return uow;
});

The downside of this registration is that the unit of work is not disposed (if needed). There is an extension package for the Simple Injector that adds RegisterPerWebRequest extension methods to the container, which will automatically ensure that the instance is disposed at the end of the web request. Using this package, you will be able to do the following registration:

container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();

Which is a shortcut to:

container.Register<IUnitOfWork, UnitOfWork>(new WebRequestLifestyle());

A Windows Forms application on the other hand, is typically single threaded (a single user will be using that application). I believe it is not unusual to have a single unit of work per form, which is disposed the form closes, but with the use of the command/handler pattern, I think it is better to take a more service oriented approach. What I mean by this is that it would be good to design it in such way that you can move the business layer to a WCF service, without the need to make changes to the presentation layer. You can achieve this by letting your commands only contain primitives and (other) DTOs. So don't store Entity Framework entities into your commands, because this will make serializing the command much harder, and it will lead to surprises later on.

When you do this, it would be convenient to create a new unit of work before the command handler starts executing, reuse that same unit of work during the execution of that handler, and commit it when the handler completed successfully (and always dispose it). This is a typical scenario for the Per Lifetime Scope lifestyle. There is an extension package that adds RegisterLifetimeScope extension methods to the container. Using this package, you will be able to do the following registration:

container.RegisterLifetimeScope<IUnitOfWork, UnitOfWork>();

Which is a shortcut to:

container.Register<IUnitOfWork, UnitOfWork>(new LifetimeScopeLifestyle());

The registration however, is just half of the story. The second part is to decide when to save the changes of the unit of work, and in the case of the use of the Lifetime Scope lifestyle, where to start and end such a scope. Since you should explicitly start a lifetime scope before the command executes, and end it when the command finished executing, the best way to do this, is by using a command handler decorator, that can wrap your command handlers. Therefore, for the Forms Application, you would typically register an extra command handler decorator that manages the lifetime scope. This approach does not work in this case. Take a look at the following decorator, but please note that it is incorrect:

private class LifetimeScopeCommandHandlerDecorator<T>
    : ICommandHandler<T>
{
    private readonly Container container;
    private readonly ICommandHandler<T> decoratedHandler;

    public LifetimeScopeCommandHandlerDecorator(...) { ... }

    public void Handle(T command)
    {
        using (this.container.BeginLifetimeScope())
        {
            // WRONG!!!
            this.decoratedHandler.Handle(command);
        }
    }
}

This approach does not work, because the decorated command handler is created before the lifetime scope is started.

We might be tempted into trying to solve this problem as follows, but that isn't correct either:

using (this.container.BeginLifetimeScope())
{
    // EVEN MORE WRONG!!!
    var handler = this.container.GetInstance<ICommandHandler<T>>();

    handler.Handle(command);
}

Although requesting an ICommandHandler<T> inside the context of a lifetime scope, does indeed inject an IUnitOfWork for that scope, the container will return a handler that is (again) decorated with a LifetimeScopeCommandHandlerDecorator<T>. Calling handler.Handle(command) will therefore result in a recursive call and we'll end up with a stack overflow exception.

The problem is that the dependency graph is already built before we can start the lifetime scope. We therefore have to break the dependency graph by deferring building the rest of the graph. The best way to do this that allows you to keep your application design clean] is by changing the decorator into a proxy and injecting a factory into it that will create the type that it was supposed to wrap. Such LifetimeScopeCommandHandlerProxy<T> will look like this:

// This class will be part of the Composition Root of
// the Windows Forms application
private class LifetimeScopeCommandHandlerProxy<T> : ICommandHandler<T>
{
    // Since this type is part of the composition root,
    // we are allowed to inject the container into it.
    private Container container;
    private Func<ICommandHandler<T>> factory;

    public LifetimeScopeCommandHandlerProxy(Container container,
         Func<ICommandHandler<T>> factory)
    {
        this.factory = factory;
        this.container = container;
    }

    public void Handle(T command)
    {
        using (this.container.BeginLifetimeScope())
        {
            var handler = this.factory();

            handler.Handle(command);        
        }
    }
}

By injecting a delegate, we can delay the time the instance is created and by doing this we delay the construction of (the rest of) the dependency graph. The trick now is to register this proxy class in such way that it will inject the wrapped instances, instead of (of course) injecting itself again. Simple Injector supports injecting Func<T> factories into decorators, so you can simply use the RegisterDecorator and in this case even the RegisterSingleDecorator extension method.

Note that the order in which decorators (and this proxy) are registered (obviously) matters. Since this proxy starts a new lifetime scope, it should wrap the decorator that commits the unit of work. In other words, a more complete registration would look like this:

container.RegisterLifetimeScope<IUnitOfWork, UnitOfWork>();

container.RegisterManyForOpenGeneric(
    typeof(ICommandHandler<>),
    AppDomain.CurrentDomain.GetAssemblies());

// Register a decorator that handles saving the unit of
// work after a handler has executed successfully.
// This decorator will wrap all command handlers.
container.RegisterDecorator(
    typeof(ICommandHandler<>),
    typeof(TransactionCommandHandlerDecorator<>));

// Register the proxy that starts a lifetime scope.
// This proxy will wrap the transaction decorators.
container.RegisterSingleDecorator(
    typeof(ICommandHandler<>),
    typeof(LifetimeScopeCommandHandlerProxy<>));

Registering the proxy and decorator the other way around would mean that the TransactionCommandHandlerDecorator<T> would depend on a different IUnitOfWork than the rest of the dependency graph does, which would mean that all changes made to the unit of work in that graph will not get committed. In other words, your application will stop working. So always review this registration carefully.

Good luck.

like image 169
Steven Avatar answered Feb 14 '23 21:02

Steven