Создание DbContext в обработчик сообщений
У меня есть DLL, которая выставляет тип как
public class MyDbContext {
[...]
}
внутри этой библиотеки у меня также есть IPackage
реализация, которая регистрирует MyDbContext
в контейнере как
public void RegisterServices( Container container ) {
container.Register<MyDbContext>( Lifestyle.Scoped );
}
Затем на эту сборку ссылаются из двух разных типов приложений: - проект web api - приложение asp.net mvc
Это инициализация проекта веб-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 - необработанное исключение 1 при обработке сообщения с идентификатором 85feff07-01a6-4195-8deb-7c8f1b62ecc3: SimpleInjector.ActivationException: MyDbContext зарегистрирован как стиль жизни веб-запроса, но экземпляр запрашивается вне контекста активная область (веб-запрос).
со следующей трассировкой стека
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]()
Я также попытался настроить Async Scoped Lifestyle в приложении mvc, но ошибка по сути та же.
1 ответ
Ребус запускает ваши обработчики в фоновом потоке, а не в потоке веб-запросов. Это означает, что невозможно использовать WebRequestLifestyle
как часть трубопровода Ребус.
Вы должны убедиться, что асинхронная область явно запущена перед выполнением обработчика. Вы можете сделать это с декоратором / прокси.
Кроме того, вы должны использовать гибридный образ жизни для вашего приложения 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.