Я использую Simple Injector для управления жизненным циклом моих внедренных зависимостей (в данном случае UnitOfWork
), и я очень доволен тем, что у меня есть отдельный декоратор, а не моя служба или обработчик команд, который занимается сохранением и удалением, что значительно упрощает код при написании бизнеса. логические уровни (я следую архитектуре, описанной в эта запись в блоге).
Вышеупомянутое работает отлично (и очень легко) с использованием пакета Simple Injector MVC NuGet и следующего кода во время создания корневого контейнера композиции, если в графе существует более одной зависимости, один и тот же экземпляр вводится во все — идеально подходит для Контекст модели Entity Framework.
private static void InitializeContainer(Container container)
{
container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
// register all other interfaces with:
// container.Register<Interface, Implementation>();
}
Теперь мне нужно запустить несколько фоновых потоков и понять из Simple Injector документация по потокам, в которой команды могут быть проксированы следующим образом:
public sealed class TransactionCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> handlerToCall;
private readonly IUnitOfWork unitOfWork;
public TransactionCommandHandlerDecorator(
IUnitOfWork unitOfWork,
ICommandHandler<TCommand> decorated)
{
this.handlerToCall = decorated;
this.unitOfWork = unitOfWork;
}
public void Handle(TCommand command)
{
this.handlerToCall.Handle(command);
unitOfWork.Save();
}
}
ThreadedCommandHandlerProxy:
public class ThreadedCommandHandlerProxy<TCommand>
: ICommandHandler<TCommand>
{
Func<ICommandHandler<TCommand>> instanceCreator;
public ThreadedCommandHandlerProxy(
Func<ICommandHandler<TCommand>> creator)
{
this.instanceCreator = creator;
}
public void Handle(TCommand command)
{
Task.Factory.StartNew(() =>
{
var handler = this.instanceCreator();
handler.Handle(command);
});
}
}
Однако из этого примера документации по потокам я вижу, что фабрики используются, если я добавлю фабрики к своим командам и сервисному уровню, все станет запутанным и несогласованным, поскольку у меня будут разные методологии сохранения для разных служб (один контейнер обрабатывает сохранение, другие экземпляры фабрик внутри сервисы обрабатывают сохранения и удаление) — вы видите, насколько понятен и прост скелет кода сервиса без каких-либо фабрик:
public class BusinessUnitCommandHandlers :
ICommandHandler<AddBusinessUnitCommand>,
ICommandHandler<DeleteBusinessUnitCommand>
{
private IBusinessUnitService businessUnitService;
private IInvoiceService invoiceService;
public BusinessUnitCommandHandlers(
IBusinessUnitService businessUnitService,
IInvoiceService invoiceService)
{
this.businessUnitService = businessUnitService;
this.invoiceService = invoiceService;
}
public void Handle(AddBusinessUnitCommand command)
{
businessUnitService.AddCompany(command.name);
}
public void Handle(DeleteBusinessUnitCommand command)
{
invoiceService.DeleteAllInvoicesForCompany(command.ID);
businessUnitService.DeleteCompany(command.ID);
}
}
public class BusinessUnitService : IBusinessUnitService
{
private readonly IUnitOfWork unitOfWork;
private readonly ILogger logger;
public BusinessUnitService(IUnitOfWork unitOfWork,
ILogger logger)
{
this.unitOfWork = unitOfWork;
this.logger = logger;
}
void IBusinessUnitService.AddCompany(string name)
{
// snip... let container call IUnitOfWork.Save()
}
void IBusinessUnitService.DeleteCompany(int ID)
{
// snip... let container call IUnitOfWork.Save()
}
}
public class InvoiceService : IInvoiceService
{
private readonly IUnitOfWork unitOfWork;
private readonly ILogger logger;
public BusinessUnitService(IUnitOfWork unitOfWork,
ILogger logger)
{
this.unitOfWork = unitOfWork;
this.logger = logger;
}
void IInvoiceService.DeleteAllInvoicesForCompany(int ID)
{
// snip... let container call IUnitOfWork.Save()
}
}
С вышеизложенным моя проблема начинает формироваться, как я понимаю из документации по времени жизни ASP .NET PerWebRequest используется следующий код:
public T GetInstance()
{
var context = HttpContext.Current;
if (context == null)
{
// No HttpContext: Let's create a transient object.
return this.instanceCreator();
}
object key = this.GetType();
T instance = (T)context.Items[key];
if (instance == null)
{
context.Items[key] = instance = this.instanceCreator();
}
return instance;
}
Вышеприведенное отлично работает для каждого HTTP-запроса, будет действительный HttpContext.Current
, однако, если я запущу новый поток с ThreadedCommandHandlerProxy
, он создаст новый поток, а HttpContext
больше не будет существовать в этом потоке.
Поскольку HttpContext
будет нулевым при каждом последующем вызове, все экземпляры объектов, внедренные в конструкторы служб, будут новыми и уникальными, в отличие от обычного HTTP для каждого веб-запроса, где объекты правильно совместно используются как один и тот же экземпляр во всех службах.
Итак, резюмируя вышесказанное в вопросы:
Как мне получить созданные объекты и внедрить общие элементы независимо от того, созданы ли они из HTTP-запроса или через новый поток?
Существуют ли какие-либо особые соображения для управления UnitOfWork
потоком в прокси-сервере обработчика команд? Как можно обеспечить его сохранение и удаление после выполнения обработчика?
Если бы у нас возникла проблема на уровне обработчика команд/уровня службы, и мы не хотели бы сохранять UnitOfWork
, мы бы просто выдали исключение? Если да, то можно ли поймать это на глобальном уровне или нам нужно перехватить исключение для каждого запроса из try
-catch
в декораторе обработчика или прокси?
Спасибо,
Крис
async
ActionResults для MVC5 эта проблема станет распространенной. - person Mrchief   schedule 14.04.2014QueueBackgroundWorkItem
, я увидел, что задача будет уничтожена, если она займет больше 90 секунд, поэтому вы подвергаетесь риску, если вам требуется больше минуты. - person KCD   schedule 21.04.2016