我正在使用 Simple Injector 来管理我注入的依赖项的生命周期(在这种情况下UnitOfWork
),我很高兴拥有一个单独的装饰器,而不是我的服务或命令处理程序来处理保存和处理,这使得编写业务逻辑时代码更容易层(我遵循这篇博文中概述的架构)。
通过在组合根容器的构造过程中使用 Simple Injector MVC NuGet 包和以下代码,上述工作完美(并且非常容易),如果图中存在多个依赖项,则将同一实例注入所有对象 - 非常适合实体框架模型上下文。
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);
});
}
}
但是,从这个线程示例文档中,我可以看到使用了工厂,如果我将工厂引入我的命令和服务层,事情会变得混乱和不一致,因为我将为不同的服务使用不同的保存方法(一个容器处理保存,其他实例化的工厂在services 处理保存和处理) - 您可以看到服务代码框架在没有任何工厂的情况下是多么清晰和简单:
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
在每次后续调用中 都将为空,因此注入服务构造函数的所有对象实例都是新的和唯一的,这与每个 Web 请求的正常 HTTP 相反,其中对象在所有服务中作为同一个实例正确共享。
因此,将以上内容总结为问题:
无论是从 HTTP 请求还是通过新线程创建的,我将如何获取构造的对象和注入的公共项目?
UnitOfWork
在命令处理程序代理中由线程管理是否有任何特殊注意事项?在处理程序执行后如何确保它被保存和处理?
如果我们在命令处理程序/服务层中遇到问题并且不想保存UnitOfWork
,我们会简单地抛出异常吗?如果是这样,是否有可能在全局级别捕获它,或者我们是否需要从处理程序装饰器或代理中的try
-中捕获每个请求的异常?catch
谢谢,
克里斯