Search code examples
c#entity-frameworkinversion-of-controlsimple-injectoreasynetq

Simple Injector Ioc DBContext with EasynetQ message life cycle


I have an issue with the EF's DBContext's life cycle using Simple Injector , I have a worker service that is running continuously but I want the DBContext to be initialized during the messages' lifespan and not for the life time of the worker service application.

There are examples on how to do this with Autofac but I am having trouble coming up with something with Simple Injector. Has anyone done this previously?

See: http://nblumhardt.com/2011/01/an-autofac-lifetime-primer/

The current implementation for our registration is:

var registration = applicationLifestyle.CreateRegistration<ProjectContext>(container);
container.AddRegistration(typeof(IProjectContext), registration);
container.AddRegistration(typeof(ProjectContext), registration);  

And inside the worker module we have:

container.RegisterModuleScoped(new CodeModule(), Lifestyle.Transient);

However we do not want to be using Lifestyle Transient for our DBContext, we want this to be alive during the message handler.

Update:

Steven, that sounds like great solution, just having some issue registering the IMessageHandler<T> e.g.

The constructor of type LifetimeScopeMessageHandlerDecorator contains the parameter of type Func<NoticeMessageHandler> with name 'decorateeFactory' that is not registered.

I have tried to register this before and tried to leave it out and many variations but no luck. Here is how I registered the decorator:

container.RegisterSingleDecorator(
    typeof(NoticeMessageHandler),
    typeof(LifetimeScopeMessageHandlerDecorator));

ERROR:

The constructor of type LifetimeScopeMessageHandlerDecorator contains the parameter of type Func<NoticeMessageHandler> with name 'decorateeFactory' that is not registered.


Solution

  • What you should do is wrapping the execution of the message with a scope. You can use the LifetimeScopeLifestyle for instance. Where to wrap this is of course completely dependent on your architecture, but I imagine you having a message handler, and in that case you can define a decorator for this:

    public class LifetimeScopeMessageHandlerDecorator<T>
        : IMessageHandler<T>
    {
        private readonly Func<IMessageHandler<T>> decorateeFactory;
        public LifetimeScopeMessageHandlerDecorator(Container container,
            Func<IMessageHandler<T>> decorateeFactory) {
            this.decorateeFactory = decorateeFactory;
        }
    
        public void Handle(T message) {
            using (this.container.BeginLifetimeScope()) {
                var decoratee = this.decorateeFactory.Invoke();
                decoratee.Handle(message);
            }
        }
    }
    

    This means that you will have to register your DbContext with the LifetimeScopeLifestyle and register your decorator. Perhaps your architecture looks differently, but the idea will always be the same: wrap the lifetime scope around the resolving and execution of the message handler.