2017-04-20 73 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