If you are applying the command/handler and query/handler patterns as described here and here, the most logical thing to do is to scope the lifetime of a DbContext
around the execution of a command and query.
This can be achieved by defining a single generic decorator that allows applying scoping:
using SimpleInjector;
using SimpleInjector.Extensions.LifetimeScoping;
public class LifetimeScopedCommandHandlerDecorator<T> : ICommandHandler<T>
{
private readonly Container container;
private readonly Func<ICommandHandler<T>> decorateeProvider;
public LifetimeScopedCommandHandlerDecorator(Container container,
Func<ICommandHandler<T>> decorateeProvider) {
this.container = container;
this.decorateeProvider = decorateeProvider;
}
public void Handle(T command) {
using (container.BeginLifetimeScope()) {
this.decorateeProvider().Handle(command);
}
}
}
This decorator can be registered as last decorator as follows:
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(LifetimeScopedCommandHandlerDecorator<>),
Lifestyle.Singleton);
After doing this, you can register your DbContext
using the LifetimeScopeLifestyle
.
You can do the same trick with query handlers.
The great advantage of this is that you allow your force strict isolation of your command handlers and query handlers, minimizing the risk of influencing each other through a shared DbContext
and it makes it easier later on to move your handlers to a different tier, by sending your command and query messages over the wire, as explained here.