在消息处理程序中创建 DbContext
DbContext creation into message handler
我有一个 dll 公开了类似
的类型
public class MyDbContext {
[...]
}
在这个库中我还有一个 IPackage
实现,它像
一样在容器中注册 MyDbContext
public void RegisterServices( Container container ) {
container.Register<MyDbContext>( Lifestyle.Scoped );
}
然后从两种不同类型的应用程序引用此程序集:
- 一个网络 api 项目
- asp.net mvc 应用程序
这是webapi项目的初始化
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 应用程序中)收到一条消息时,容器会尝试像这样初始化一个消息处理程序
public class MyHandler : BaseMessageHandler, IHandleMessages<MyMessage>, IHandleMessages<IFailed<MyMessage>>
{
public MyHandler( ILog logger, MyDbContext context ) {
_logger = logger;
_context = context;
}
}
但是我收到一条错误消息说
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.
具有以下堆栈跟踪
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 应用程序中设置 Async Scoped Lifestyle,但错误基本相同。
Rebus 在后台线程上运行您的处理程序,而不是在 Web 请求线程上。这意味着不可能将 WebRequestLifestyle
用作 Rebus 管道的一部分。
您应该确保在执行处理程序之前显式启动异步作用域。您可以使用 decorator/proxy.
最重要的是,您应该为 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 项目中注册这个装饰器。
我有一个 dll 公开了类似
的类型public class MyDbContext {
[...]
}
在这个库中我还有一个 IPackage
实现,它像
MyDbContext
public void RegisterServices( Container container ) {
container.Register<MyDbContext>( Lifestyle.Scoped );
}
然后从两种不同类型的应用程序引用此程序集: - 一个网络 api 项目 - asp.net mvc 应用程序
这是webapi项目的初始化
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 应用程序中)收到一条消息时,容器会尝试像这样初始化一个消息处理程序
public class MyHandler : BaseMessageHandler, IHandleMessages<MyMessage>, IHandleMessages<IFailed<MyMessage>>
{
public MyHandler( ILog logger, MyDbContext context ) {
_logger = logger;
_context = context;
}
}
但是我收到一条错误消息说
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.
具有以下堆栈跟踪
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 应用程序中设置 Async Scoped Lifestyle,但错误基本相同。
Rebus 在后台线程上运行您的处理程序,而不是在 Web 请求线程上。这意味着不可能将 WebRequestLifestyle
用作 Rebus 管道的一部分。
您应该确保在执行处理程序之前显式启动异步作用域。您可以使用 decorator/proxy.
最重要的是,您应该为 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 项目中注册这个装饰器。