Ответ 1
Кажется, когда я запускаю задачу в отдельном потоке в ASP.NET MVC, SimpleInjector создает новый экземпляр DbContext для каждого вызова.
Поведение стиля RegisterPerWebRequest
Simple Injector v1.5 и ниже - это возвращение временного экземпляра, когда экземпляры запрашиваются вне контекста веб-запроса (где HttpContext.Current
равно null). Возвращение временного экземпляра было ошибкой дизайна в Simple Injector, так как это облегчает скрытие неправильного использования. Версия 1.6 простого инжектора выведет исключение вместо неправильного возврата временного экземпляра, чтобы четко сообщить, что вы неправильно сконфигурировали контейнер.
Хотя некоторые библиотеки IoC (например, StructureMap) имеют смешанные образ жизни для каждого потока на веб-запрос, кажется, простой инжектор не имеет ни одного
Правильно, что Simple Injector не имеет встроенной поддержки смешанного образа жизни из-за нескольких причин. Прежде всего, это довольно экзотическая особенность, которой мало кто нуждается. Во-вторых, вы можете смешивать любые два или три образа жизни вместе, так что это будет почти бесконечное сочетание гибридов. И последнее, это (довольно) легко зарегистрировать это самостоятельно.
Хотя вы можете смешать за веб-запрос с помощью Per Thread образ жизни, вероятно, было бы лучше, если вы смешиваете Per Web Request с Per Lifetime Scope, так как с помощью Lifetime Scope вы явно запускаете и завершаете область (и может удалять DbContext
, когда область действия заканчивается).
Из Simple Injector 2 и дальше вы можете легко смешивать любое количество образы жизни, используя Lifestyle.CreateHybrid. Вот пример:
var hybridLifestyle = Lifestyle.CreateHybrid(
() => HttpContext.Current != null,
new WebRequestLifestyle(),
new LifetimeScopeLifestyle());
// Register as hybrid PerWebRequest / PerLifetimeScope.
container.Register<DbContext, MyDbContext>(hybridLifestyle);
Есть еще один вопрос о Stackoverflow, который затрагивает эту тему немного глубже, вы можете взглянуть: Простой инжектор: многопоточность в MVC3 ASP.NET
UPDATE
О вашем обновлении. Вы почти там. Команды, выполняемые в фоновом потоке, должны выполняться в пределах Lifetime Scope, поэтому вам нужно будет запустить его явно. Трюк здесь заключается в вызове BeginLifetimeScope
в новом потоке, но до того, как будет создан фактический обработчик команд (и его зависимости). Другими словами, лучший способ сделать это - внутри декоратора.
Самое простое решение - обновить ваш AsyncCommandHandlerDecorator
, чтобы добавить область:
public class AsyncCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand> where TCommand : ICommand
{
private readonly Container _container;
private readonly Func<ICommandHandler<TCommand>> _factory;
public AsyncCommandHandlerDecorator(Container container,
Func<ICommandHandler<TCommand>> factory)
{
_container = container;
_factory = factory;
}
public void Handle(TCommand command)
{
ThreadPool.QueueUserWorkItem(_ =>
{
using (_container.BeginLifetimeScope())
{
// Create new handler in this thread
// and inside the lifetime scope.
var handler = _factory();
handler.Handle(command);
}
});
}
}
Пуристы, выступающие за принципы SOLID, будут кричать, что этот класс нарушает Принцип единой ответственности, так как этот декоратор выполняет команды в новом потоке и запускает новую сферу жизни. Я бы не стал беспокоиться об этом, так как я думаю, что существует тесная взаимосвязь между запуском фонового потока и началом жизненного цикла (вы не использовали бы его без другого в любом случае). Но все же вы можете легко оставить AsyncCommandHandlerDecorator
нетронутым и создать новый LifetimeScopedCommandHandlerDecorator
следующим образом:
public class LifetimeScopedCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand> where TCommand : ICommand
{
private readonly Container _container;
private readonly Func<ICommandHandler<TCommand>> _factory;
public LifetimeScopedCommandHandlerDecorator(Container container,
Func<ICommandHandler<TCommand>> factory)
{
_container = container;
_factory = factory;
}
public void Handle(TCommand command)
{
using (_container.BeginLifetimeScope())
{
// The handler must be created inside the lifetime scope.
var handler = _factory();
handler.Handle(command);
}
}
}
Порядок, в котором эти декораторы зарегистрированы, конечно, необходим, так как AsyncCommandHandlerDecorator
должен обернуть LifetimeScopedCommandHandlerDecorator
. Это означает, что регистрация LifetimeScopedCommandHandlerDecorator
должна быть первой:
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(LifetimeScopedCommandHandlerDecorator<>),
backgroundCommandCondition);
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(AsyncCommandHandlerDecorator<>),
backgroundCommandCondition);
fooobar.com/info/375214/... более подробно об этом говорит. Вы должны обязательно взглянуть.