c# - How to use single object and fill its properties throughout all sub process in single webapi request? -


i have asp.net webapi project , using simple injector dependency injection.

i have messages, message handlers , decorators on project. using decorator pattern.

i need keep details of each process , need save them database.for example, want keep details : when request started, happened on sub process , when process finished, request body , response body etc. first need write details object, , save object database.

imagine process detail keeper class name processdetail. has properties requestbody, responsebody, starttime, endtime etc...

how keep same processdetail object, each steps of decorator pattern , fill each process ?

in following code, message (command) come server webapi request. , command processor handle command , executes related command handler. first , required decorators executing validating, tracing, transactional decorators executing. message command handler executing.

for example, want keep same processdetail object processes. requirement about, when validate handler executed, want write processdetail object, when tracing handler executed or executing , want write same processdetail object (i mean instance) , when related command handler executing or executed , want write same processdetail object.

as result, need single processdetail object instance fill sub process. main idea is, taking details executing parts , write these details processdetail object instance. , write processdetails object instance database via entity framework or way.

my code sample here :

commandhandler

public interface icommandhandler<in tcommand, out tresult>     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>     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>     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>     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 processdetail object sort of entity want persist in database (using ef or o/rm of sort).

how this, depends bit on exact scenario. in case command handlers don't nested (my prefered approach) , don't have store object in case of failure, decorator might 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);      // responsebody not can set @ state.     detail.endtime = datetime.now;      return result; } 

if responsebody should contain response sent client, not yet available @ time decorator executed. if need store data, have use different interception point. instance web api message handler.

in case need store object in case of failure, can't operate within same dbcontext, because dbcontext in invalide state, because have unsaved changes, don't want persist. in case need abstract logging logic , within extracted component need create new dbcontext use component:

public tresult handle(tcommand command) {     datetime starttime = datetime.now;     exception exception = null;      try {         return _innerhandler.handle(command);     }     catch (exception ex) {         exception = ex;         throw;     }     {         _requestlogger.log(command, starttime, exception);     } } 

now request logger component can create processdetail instance , store inside own transaction.

in case command handlers nested, means decorators nested well. there multiple ways handle this. instance register request logger per-request lifestyle , let log once per request. or can start lifetime scope or execution context scope within decorator , same. or can let decorator detect being called while wrapped , in case let 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) {     // before } 

the scopecounter created follows:

public class scopecounter<t> {     private int count;     public idisposable beginscope() {         this.count++;         return new decounter { counter = };     }     public bool isouterscope { { 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 class per request:

container.registeropengeneric(typeof(scopecounter<>), typeof(scopecounter<>),     new webapirequestlifestyle()); 

few notes on design though:

  • don't make icommandhandler varient (with in , out keywords), unless planning use this. commands map 1 handler, , in case don't need variance. in , out keywords confusing , misleading in case.
  • you should throw; instead of throw ex; in both tracingcommandhandlerdecorator , transactionalcommandhandlerdecorator prevent losing great part of stack trace.

Comments

Popular posts from this blog

asp.net mvc - SSO between MVCForum and Umbraco7 -

Python Tkinter keyboard using bind -

ubuntu - Selenium Node Not Connecting to Hub, Not Opening Port -