2017-04-20 67 views
4

我有一个DLL至极露出型等的DbContext创建到消息处理器

public class MyDbContext { 
    [...] 
} 

这个库里面我也有一个IPackage实现这在容器注册MyDbContext

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

该组件然后引用两种不同类型的应用程序: - 一个web api项目 - 一个asp.net mvc应用程序

这是web API项目

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

的初始化,这是MVC应用

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

的初始化当我接收来自Rebus的队列的消息(在MVC应用程序)的容器尝试instatiate这样

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

消息处理程序,但我收到一个错误说

Rebus.Retry.ErrorTracking.InMemErrorTracker - 未处理的异常1,同时处理消息ID 85feff07-01a6-4195-8deb-7c8f1b62ecc3:SimpleInjector.ActivationException:该MyDbContext被注册为“网络请求的生活方式,但实例在活动(Web请求)范围的上下文之外请求。

与以下堆栈跟踪

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]() 

我还试图以设置异步作用域服饰在MVC应用程序,但该错误基本上是相同的。

回答

3

Rebus在后台线程上运行您的处理程序,而不是在Web请求线程上运行。这意味着不可能使用WebRequestLifestyle作为Rebus管道的一部分。

您应该确保在执行处理程序之前明确启动了异步作用域。你可以用装饰器/代理来做到这一点。

最重要的是,您应该为您的MVC应用程序使用混合生活方式,因为MVC使用WebRequestLifestyle而不是`AsyncScopedLifestyle。

你可以在你的MVC应用程序中应用的混合动力生活方式如下:

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

你的装饰应如下所示:

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); 
     } 
    } 
} 

如下您可以注册你的装饰:

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

你应该在MVC和你的Web API项目中注册这个装饰器吨。

+0

嗨史蒂文,感谢您的及时答复。您能否澄清我需要对我的MessageHandler进行哪些修改?其实我只是注册我的处理程序为'container.Register ,MyMessageHandler>(Lifestyle.Transient)'。我应该改变它吗? – Lorenzo

+2

嗨@Lorenzo,你不必改变任何东西到你的消息处理程序或你的注册;这就是Simple Injector的美妙结合了Rebus提供的精心设计的抽象。你只需将你的装饰器注册为最后的装饰器(如果你有更多的),它就应该开始工作。 – Steven

+0

伟大的质量答案。谢谢! – Lorenzo