将 DbContext 创建到消息处理程序中

问题描述 投票:0回答:1

我有一个 dll,它公开了类似的类型

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 应用程序中)收到消息时,容器尝试实例化这样的消息处理程序

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 应用程序中设置异步作用域生活方式,但错误基本相同。

c# simple-injector rebus
1个回答
4
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.