This project is read-only.

Automatic creation of Unit of Work

Jun 14, 2013 at 10:16 AM
Assume I implement my command handlers in the spirit of your blog. To perform their work, the take a dependency on the unit of work. In my case, this is ISession from NHibernate.

Because the handlers take it as a dependency, Simple Injector needs to be able to resolve it. To achieve this, I perform this registration:
container.RegisterSingle(() => sessionFactoryFactory.Create(connectionString));
container.RegisterLifetimeScope(() => container.GetInstance<ISessionFactory>()
                                               .OpenSession());
That results in the exact same problem as in the previous discussion: Verifying the container tries to access the database.

However, the Options 1 and 2 you provided in that discussion won't help here, as we are not dealing with a singleton instance.
Option 3 would work, but I have the feeling I am missing something. How did you solve this problem in your applications?
Jun 14, 2013 at 11:19 AM
With Entity Framework and SQL to SQL these problems do not exist, because they would only open the connection on the first request, not when creating the unit of work. If you read the Framework Design Guidelines you see that this is a pattern they describe explicitly: The constructor should do as little as possible. This especially holds when doing DI, since we want to seperate construction and use.

So in a sense, I think NHibernate breaks this rule and that's why this is troubling you.

I can think of all sorts of solutions, for instance by letting a decorator start the session (instead of the registration), or extend the registration to check to see if it runs during verification, but I would personally go for Option 3.
Jun 14, 2013 at 11:31 AM
I don't think that the actual work is performed in the constructor of the session but in the call to OpenSession. Apparently it tries to access the database.

Anyway, I, too, thought about using a decorator, but I am unsure on how I would do that. The command handlers have a dependency on the session. If I use a decorator to create the session, how would it get injected into the command handler?
Surely, I could remove the direct dependency on the session, but how would the command handlers access the session then?

Option 3 is my fallback, but I would really like to see how a decorator could solve this - just for the sake of learning :-)
Jun 14, 2013 at 12:13 PM
For the sake of learning, this is what to can do with a decorator:
container.RegisterSingle<ISessionFactory>(
    sessionFactoryFactory.Create(connectionString));
    
// using SimpleInjector.Advanced
container.Register<ISession>(() =>
    container.GetInstance<DecoratorSessionScope>().Session ??
        (container.IsVerifying() ? new DummySession() : null));

container.RegisterDecorator(typeof(ICommandHandler<>),
    typeof(BeginSessionCommandHandlerDecorator<>));
    
container.RegisterLifetimeScope<DecoratorSessionScope>();
    
public class DecoratorSessionScope
{
    public ISession Session { get; set; }
}

public class BeginSessionCommandHandlerDecorator<T>
    : ICommandHandler<T>
{
    private readonly ICommandHandler<T> decoratee;
    private readonly ISessionFactory sessionFactory;
    private readonly DecoratorSessionScope sessionScope;
    
    public BeginSessionCommandHandlerDecorator(
        ICommandHandler<T> decoratee,
        ISessionFactory sessionFactory,
        DecoratorSessionScope sessionScope)
    {
        this.decoratee = decoratee;
        this.sessionFactory = sessionFactory;
        this.sessionScope = sessionScope;
    }

    public void Handle(T command)
    {
        using (var session = this.sessionFactory.OpenSession())
        {
            // set the session for the rest
            this.sessionScope.Session = session;
        
            this.decoratee.Handle(command);
        }
    }
}
Basically, the trick is to have an intermediate object, DecoratorSessionScope in this case, that you use both in the decorator to store the created session, and in the registration to get the actual session.

But the problem can also be solved as follows:
var factory = sessionFactoryFactory.Create(connectionString);

// using SimpleInjector.Advanced;   
container.RegisterLifetimeScope<ISession>(() =>
    container.IsVerifying() ? new DummySession() : factory.OpenSession());
Downside is that the SimpleInjector.Advanced.AdvancedExtensions.IsVerifying(Container) extension method always takes a lock, but for the performance of your application this will most likely not be an issue.
Marked as answer by dot_NET_Junkie on 2/26/2014 at 1:37 PM
Jun 14, 2013 at 12:20 PM
Thanks, that looks nice :-)