【问题标题】:DbContext creation into message handlerDbContext 创建到消息处理程序
【发布时间】:2017-04-20 11:31:04
【问题描述】:

我有一个 dll 暴露了一个像

这样的类型
public class MyDbContext {
    [...]
}

在这个库中,我还有一个IPackage 实现,它在容器中注册MyDbContext,如

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

然后从两种不同类型的应用程序引用此程序集: - 一个网络 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 应用程序中)收到消息时,容器会尝试像这样实例化消息处理程序

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

但我收到一条错误提示

Rebus.Retry.ErrorTracking.InMemErrorTracker - 处理 ID 为 85feff07-01a6-4195-8deb-7c8f1b62ecc3 的消息时未处理的异常 1:SimpleInjector.ActivationException:MyDbContext 已注册为“Web 请求”生活方式,但在外部请求实例活动(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 应用程序中设置 Async Scoped Lifestyle,但错误基本相同。

【问题讨论】:

    标签: c# simple-injector rebus


    【解决方案1】:

    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 项目中注册这个装饰器。

    【讨论】:

    • 嗨史蒂文,感谢您的及时答复。您能否澄清我需要对我的 MessageHandler 进行哪些修改?实际上我只是将我的处理程序注册为container.Register&lt;IHandleMessages&lt;MyMessage&gt;, MyMessageHandler&gt;( Lifestyle.Transient )。我应该改变它吗?
    • 嗨@Lorenzo,您不必对消息处理程序或注册进行任何更改;这就是 Simple Injector 与 Rebus 提供的精心设计的抽象相结合的美妙之处。您只需将您的装饰器注册为最后一个装饰器(如果您有更多),它就应该开始工作了。
    • 高质量的答案。谢谢!
    • 一个小回归...在进行了建议的更改之后,现在我得到了一个不同的错误System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. 这是什么?
    • @Lorenzo:对不起。我忘记了await。请查看更新后的装饰器。
    猜你喜欢
    • 2014-02-26
    • 1970-01-01
    • 1970-01-01
    • 2013-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多