5

I have a dll wich expose a type like

public class MyDbContext {
    [...]
}

inside this library I also have an IPackage implementation which register the MyDbContext in the container like

public void RegisterServices( Container container ) {
    container.Register<MyDbContext>( Lifestyle.Scoped );
}

This assembly is then referenced from two different types of applications: - a web api project - an asp.net mvc application

This is the initialization of the web api project

var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
InitializeContainer( container );
container.RegisterWebApiControllers( GlobalConfiguration.Configuration );
container.Verify();

and this is the initialization of the mvc application

var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
InitializeContainer( container );
container.RegisterMvcControllers( Assembly.GetExecutingAssembly() );
container.Verify();

When I receive a message from a Rebus queue (in the mvc application) the container try to instatiate a message handler like this

public class MyHandler : BaseMessageHandler, IHandleMessages<MyMessage>, IHandleMessages<IFailed<MyMessage>>
{
    public MyHandler( ILog logger, MyDbContext context ) {
        _logger = logger;
        _context = context;
    }
}

but I receive an error saying

Rebus.Retry.ErrorTracking.InMemErrorTracker - Unhandled exception 1 while handling message with ID 85feff07-01a6-4195-8deb-7c8f1b62ecc3: SimpleInjector.ActivationException: The MyDbContext is registered as 'Web Request' lifestyle, but the instance is requested outside the context of an active (Web Request) scope.

with the following stack trace

at SimpleInjector.Scope.GetScopelessInstance[TImplementation](ScopedRegistration`1 registration)
at SimpleInjector.Scope.GetInstance[TImplementation](ScopedRegistration`1 registration, Scope scope)
at SimpleInjector.Advanced.Internal.LazyScopedRegistration`1.GetInstance(Scope scope)
at lambda_method(Closure )
at SimpleInjector.InstanceProducer.GetInstance()
at SimpleInjector.Container.GetInstance[TService]()

I have also tried to setup the Async Scoped Lifestyle in the mvc application but the error is substantially the same.

Steven
  • 166,672
  • 24
  • 332
  • 435
Lorenzo
  • 29,081
  • 49
  • 125
  • 222

1 Answers1

4

Rebus runs your handlers on a background thread, not on a web request thread. This means that it is impossible to use the WebRequestLifestyle as part of the Rebus pipeline.

You should make sure that an async scope is explicitly started before a handler is executed. You can do that with a decorator/proxy.

On top of that, you should use a hybrid lifestyle for your MVC application, because MVC uses WebRequestLifestyle instead of `AsyncScopedLifestyle.

You can apply your hybrid lifestyle in your MVC application as follows:

container.Options.DefaultScopedLifestyle = Lifestyle.CreateHybrid(
    defaultLifestyle: new AsyncScopedLifestyle(),
    fallbackLifestyle: new WebRequestLifestyle());

Your decorator should look as follows:

public sealed class AsyncScopedMessageHandler<T> : IHandleMessages<T>
{
    private readonly Container container;
    private readonly Func<IHandleMessages<T>> decorateeFactory;
    public AsyncScopedMessageHandler(Container container, Func<IHandleMessages<T>> factory)
    {
        this.container = container;
        this.decorateeFactory = factory;
    }
    public async Task Handle(T message) {
        using (AsyncScopedLifestyle.BeginScope(this.container)) {
            var decoratee = this.decorateeFactory.Invoke();
            await decoratee.Handle(message);
        }
    }
}

You can register your decorator as follows:

container.RegisterDecorator(
    typeof(IHandleMessages<>),
    typeof(AsyncScopedMessageHandler<>),
    Lifestyle.Singleton);

You should register this decorator both in MVC and your Web API project.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Hi Steven, thanks for your prompt answer. Could you please clarify which modification I need to make to my MessageHandler?. Actually I simply register my handler as `container.Register, MyMessageHandler>( Lifestyle.Transient )`. Should I change it? – Lorenzo Apr 20 '17 at 12:06
  • 2
    Hi @Lorenzo, you don't have to change anything to your message handler or your registration; that's the beauty of Simple Injector combined with a well designed abstraction that Rebus provides. You just register your decorator as last decorator (if you have more) and it should just start to work. – Steven Apr 20 '17 at 12:11
  • Great quality answer .Thank you! – Lorenzo Apr 20 '17 at 12:20
  • A small regression... after having made the suggested changes now I get a different error `System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.` What is this? – Lorenzo Apr 20 '17 at 12:41
  • 1
    @Lorenzo: I'm sorry. I forgot the `await`. Please see the updated decorator. – Steven Apr 20 '17 at 13:37