I have an Asp.net WebApi Project and i am using Simple Injector for dependency injection.
I have some messages, message handlers , decorators on my project. I am using decorator pattern.
I need to keep details of each process and i need to save them to database.For example, i want to keep details like : when request started, what happened on sub process and when process finished, what was the request body , what was the response body etc. First i need to write details to an object, and then i will save this object to database.
Imagine that this process detail keeper class name is ProcessDetail. So it has some properties RequestBody, ResponseBody, StartTime, EndTime etc...
How to keep same ProcessDetail object, for each steps of the decorator pattern and fill it for each process ?
In following code, message (command) come to server as webapi request. And command processor handle this command and executes related command handler. But first , required decorators executing like validating, tracing, transactional decorators executing. Finally message command handler executing.
For example, i want to keep same ProcessDetail object for all this processes. My requirement is about, when validate handler executed, i want to write something to ProcessDetail object, then when tracing handler executed or executing , i want to write something to same ProcessDetail object (i mean instance) and finally when related command handler executing or executed , i want to write something to same ProcessDetail object.
As a result, i need a single ProcessDetail object instance to fill it for all sub process. The main idea is, taking some details from all executing parts and write these details into to ProcessDetail object instance. And finally i will write this ProcessDetails object instance to database via entity framework or another way.
My code sample here :
CommandHandler
public interface ICommandHandler<in TCommand, out TResult>
where TCommand : ICommand<TResult>
{
TResult Handle(TCommand command);
}
CommandProcessor
public interface ICommandProcessor
{
object Process(object command);
TResult Process<TResult>(ICommand<TResult> command);
}
Command
public interface ICommand<TResult>
{
}
TracingDecorator
public class TracingCommandHandlerDecorator<TCommand, TResult>
: ICommandHandler<TCommand, TResult>
where TCommand : ICommand<TResult>
{
private readonly ICommandHandler<TCommand, TResult> _innerHandler;
public TracingCommandHandlerDecorator(
ICommandHandler<TCommand, TResult> innerHandler)
{
_innerHandler = innerHandler;
}
public TResult Handle(TCommand command)
{
try
{
Debug.Write(command);
var result = _innerHandler.Handle(command);
Debug.Write(result);
return result;
}
catch (Exception ex)
{
Debug.Write(ex);
throw ex;
}
}
}
Validating Decorator
public class ValidatingCommandHandlerDecorator<TCommand, TResult>
: ICommandHandler<TCommand, TResult>
where TCommand : ICommand<TResult>
{
private readonly ICommandHandler<TCommand, TResult> _innerHandler;
public ValidatingCommandHandlerDecorator(
ICommandHandler<TCommand, TResult> innerHandler)
{
_innerHandler = innerHandler;
}
public TResult Handle(TCommand command)
{
var context = new ValidationContext(command, null);
Validator.ValidateObject(command, context);
return _innerHandler.Handle(command);
}
}
TransactionalCommand Decorator
public class TransactionalCommandHandlerDecorator<TCommand, TResult>
: ICommandHandler<TCommand, TResult>
where TCommand : ICommand<TResult>
{
private readonly ICommandHandler<TCommand, TResult> _innerHandler;
private readonly OneDeskDbContext _dbContext;
public TransactionalCommandHandlerDecorator(
ICommandHandler<TCommand, TResult> innerHandler,
OneDeskDbContext dbContext)
{
_innerHandler = innerHandler;
_dbContext = dbContext;
}
public TResult Handle(TCommand command)
{
try
{
var result = _innerHandler.Handle(command);
_dbContext.Commit();
return result;
}
catch (Exception ex)
{
_dbContext.Rolback();
throw ex;
}
}
}
Global.asax
Container = new Container();
Container.RegisterSingle<ICommandProcessor, IocCommandProcessor>();
Container.RegisterManyForOpenGeneric(typeof(ICommandHandler<,>),
Assembly.GetExecutingAssembly());
Container.RegisterDecorator(typeof(ICommandHandler<,>),
typeof(TransactionalCommandHandlerDecorator<,>));
Container.RegisterDecorator(typeof(ICommandHandler<,>),
typeof(TracingCommandHandlerDecorator<,>));
Container.RegisterDecorator(typeof(ICommandHandler<,>),
typeof(ValidatingCommandHandlerDecorator<,>));
Container.RegisterSingle<ISmsService, DummpySmsService>();
Container.RegisterWebApiRequest<OneDeskDbContext, OneDeskDbContext>();
Container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
Container.Verify();
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(Container);
I assume this ProcessDetail
object is some sort of entity that you want to persist in your database (using EF or another O/RM of some sort).
How to do this, depends a bit on your exact scenario. In case command handlers don't get nested (my prefered approach) and you don't have to store the object in case of a failure, a decorator might look as follows:
public TResult Handle(TCommand command) {
var detail = new ProcessDetail {
StartTime = DateTime.Now,
RequestBody = HttpContext.Current.Request.RawUrl
};
dbContext.ProcessDetails.Add(detail);
var result = _innerHandler.Handle(command);
// The ResponseBody is not something you can set at this state.
detail.EndTime = DateTime.Now;
return result;
}
If the ResponseBody
should contain the response that is sent back to the client, this is something that is not yet available at the time your decorator is executed. If you need to store this data, you will have to use a different interception point. For instance a Web API message handler.
In case you need to store the object as well in case of a failure, you can't operate within the same dbContext
, because that dbContext will be in an invalide state, because it is likely to have unsaved changes, that you don't want to persist. In that case you need to abstract the logging logic and within that extracted component need to create a new dbcontext that you only use for that component:
public TResult Handle(TCommand command) {
DateTime startTime = DateTime.Now;
Exception exception = null;
try {
return _innerHandler.Handle(command);
}
catch (Exception ex) {
exception = ex;
throw;
}
finally {
_requestLogger.Log(command, startTime, exception);
}
}
Now the request logger component can create the ProcessDetail
instance and store it inside its own transaction.
In case command handlers are nested, this means that decorators get nested as well. There are multiple ways to handle this. You could for instance register the request logger with a per-request lifestyle and let it log only once per request. Or you can start an lifetime scope or execution context scope within a decorator and do the same. Or you can let the decorator detect that it is being called while wrapped and in that case just let it do nothing:
private readonly ScopeCounter<RequestLogCommandHandlerDecorator> scopeCounter;
public TResult Handle(TCommand command) {
using (this.scopeCounter.BeginScope()) {
if (this.scopeCounter.IsOuterScope) {
return HandleWithRequestLog(command);
} else {
return _innerHandler.Handle(command);
}
}
}
private TResult HandleWithRequestLog(TCommand command) {
// some as before
}
The ScopeCounter could be created as follows:
public class ScopeCounter<T> {
private int count;
public IDisposable BeginScope() {
this.count++;
return new Decounter { counter = this };
}
public bool IsOuterScope { get { return this.count == 1; } }
private sealed class Decounter : IDisposable {
internal ScopeCounter<T> counter;
public void Dispose() {
if (counter != null) {
counter.count--;
this.counter = null;
}
}
}
}
You can register this class per request:
container.RegisterOpenGeneric(typeof(ScopeCounter<>), typeof(ScopeCounter<>),
new WebApiRequestLifestyle());
Few notes on your design though:
throw;
instead of throw ex;
in both your TracingCommandHandlerDecorator and TransactionalCommandHandlerDecorator to prevent losing a great part of your stack trace.