Я использую Simple Injector для управления временем жизни моих внедренных зависимостей (в данном случае UnitOfWork
), и я очень доволен тем, что у меня есть отдельный декоратор, а не чем моя служба или обработчик команд, следящий за сохранением и удалением, делает код намного проще при написании слоев бизнес-логики (я следую архитектуре, описанной в этом сообщении в блоге).
Вышеупомянутое работает отлично (и очень легко) с использованием пакета Simple Injector MVC NuGet и следующего кода во время построения корневого контейнера композиции, если в графе существует более одной зависимости, один и тот же экземпляр внедряется во все - идеально подходит для контекста модели Entity Framework.
private static void InitializeContainer(Container container)
{
container.RegisterPerWebRequest();
// register all other interfaces with:
// container.Register();
}
Теперь мне нужно запустить несколько фоновых потоков и понять из документации Simple Injector по потокам, что команды могут быть проксированы следующим образом:
public sealed class TransactionCommandHandlerDecorator
: ICommandHandler
{
private readonly ICommandHandler handlerToCall;
private readonly IUnitOfWork unitOfWork;
public TransactionCommandHandlerDecorator(
IUnitOfWork unitOfWork,
ICommandHandler decorated)
{
this.handlerToCall = decorated;
this.unitOfWork = unitOfWork;
}
public void Handle(TCommand command)
{
this.handlerToCall.Handle(command);
unitOfWork.Save();
}
}
ThreadedCommandHandlerProxy:
public class ThreadedCommandHandlerProxy
: ICommandHandler
{
Func> instanceCreator;
public ThreadedCommandHandlerProxy(
Func> creator)
{
this.instanceCreator = creator;
}
public void Handle(TCommand command)
{
Task.Factory.StartNew(() =>
{
var handler = this.instanceCreator();
handler.Handle(command);
});
}
}
см., что фабрики используются, если я добавлю фабрики к своим командам, а уровень обслуживания будет запутан и непоследовательен, поскольку у меня будут разные методологии сохранения для разных служб (один контейнер обрабатывает сохранение, другие созданные экземпляры фабрик в службах обрабатывают сохранение и удаление) - вы можете посмотрите, насколько понятен и прост скелет кода службы без каких-либо фабрик:
public class BusinessUnitCommandHandlers :
ICommandHandler,
ICommandHandler
{
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
будет иметь значение null при каждом последующем вызове, все экземпляры объектов, внедренные в конструкторы сервисов, будут новыми и уникальными, в отличие от обычного HTTP для каждого веб-запроса, где объекты совместно используются правильно как одни и те же. во всех службах.
Итак, резюмируя вышеизложенное в виде вопросов:
Как мне получить сконструированные объекты и внедрить общие элементы независимо от того, созданы ли они из HTTP-запроса или через новый поток?
Существуют ли какие-либо особые соображения относительно UnitOfWork
, управляемого потоком в прокси-сервере обработчика команд? Как можно обеспечить его сохранение и удаление после выполнения обработчика?
Если бы у нас возникла проблема на уровне обработчика команд/уровня службы и мы не хотели бы сохранять UnitOfWork
, мы бы просто выдали исключение? Если да, то можно ли поймать это на глобальном уровне или нам нужно перехватывать исключение для каждого запроса из try
- catch
в декораторе обработчика или прокси?
Спасибо,
Крис