I am using Simple Injector to manage the lifetime of my injected dependencies (in this case UnitOfWork
), and I am very happy as having a separate decorator rather than my service or command handler looking after saving and disposing makes code a lot easier when writing business logic layers (I follow the architecture that is outlined in this blog post).
The above is working perfectly (and very easily) by using the Simple Injector MVC NuGet package and the following code during the construction of the composition root container, if more than one dependency exists in the graph the same instance is injected across all - perfect for Entity Framework model context.
private static void InitializeContainer(Container container)
{
container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
// register all other interfaces with:
// container.Register<Interface, Implementation>();
}
I now need to run some background threads and understand from Simple Injector documentation on threads that commands can be proxied as follows:
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);
});
}
}
However, from this threading sample documentation I can see factories are used, if I introduce factories to my commands and service layer things will get confused and inconsistent as I will have different saving methodologies for different services (one container handles saving, other instantiated factories within services handle saves and disposing) - you can see how clear and simple the service code skeleton is without any factories:
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()
}
}
With the above my problem starts to form, as I understand from the documentation on ASP .NET PerWebRequest lifetimes, the following code is used:
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;
}
The above works fine for each HTTP request there will be a valid HttpContext.Current
, however if I spin-up a new thread with the ThreadedCommandHandlerProxy
it will create a new thread and the HttpContext
will no longer exist within that thread.
Since the HttpContext
would be null on each subsequent call, all instances of objects injected into service constructors would be new and unique, the opposite to normal HTTP per web request where objects are shared correctly as the same instance across all services.
So to summarize the above into questions:
How would I go about getting the objects constructed and common items injected regardless of whether created from HTTP request or via a new thread?
Are there any special considerations for having a UnitOfWork
managed by a thread within a command handler proxy? How can one ensure it is saved and disposed of after the handler has executed?
If we had a problem within the command-handler/service-layer and didn't want to save the UnitOfWork
, would we simply throw an exception? If so, is it possible to catch this at a global level or do we need to catch the exception per request from within a try
-catch
in the handler decorator or proxy?
Thanks,
Chris
See Question&Answers more detail:
os