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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With