Callbacks hook come RubyOnRails in C#

In questi giorni di calma apparente mi sto guardando un po' di RubyOnRails. In questo framework ho trovato cose interessanti. Una tra le tante è la possibilità di definire nel Model dei metodi in modo tale che vengano invocati prima e dopo una determinata azione. Es. è possibile indicare un metodo come azione before_save in modo che venga eseguito prima del metodo save. 
Sono presenti parecchie possibilità tra cui:

Maggiori info a riguardo le trovate qui.

Ora mi sono chiesto come è possibile avere una funzionalità simile in C#. Ho aperto VisualStudio è sono giusto a questa soluzione. E' solo una prima bozza, sicuramente si può fare di meglio!
Partiamo per esempio da un piccolo pezzo di codice che salva un oggetto Task così:

   1: Task task = new Task(1, DateTime.Today, "Demo task");
   2: IModel model = new TaskModel();
   3: model.Save(task);

ora quello che vorrei ottenere è la possibilità di eseguire un metodo prima ed uno metodo dopo il Save. La prima soluzione sfrutta una mia classe Hook in cui ho definito un metodo che accetta come parametri il metodo da invocare, il parametro per questo metodo e successivamente 2 delegate ai metodi che voglio eseguire come before_save e after_save:

   1: public class Hook
   2: {
   3:     public delegate void Action<A>(A a);
   4:  
   5:     public static void Do<A>(Action<A> action, A a, Action beforeAction, Action afterAction)
   6:     {
   7:         beforeAction();
   8:         action(a);
   9:         afterAction();
  10:     }
  11: }

Poi viene usata così:

   1: Task task = new Task(1, DateTime.Today, "Demo task");
   2: IModel model = new TaskModel();
   3: Hook.Do(model.Save, task, model.ActionsBeforeSave, model.ActionsAfterSave);

Partendo da questa prima soluzione ho cercato di sfruttare l'uso deglil attributi in modo tale da rendere il mio codice più semplice e leggibile. La seconda soluzione sfrutta quindi oltre ai delegate anche gli attributi da me creati Before e After con cui viene decorato il metodo Save

   1: public class TaskModel
   2: {
   3:     readonly Storage _storage;
   4:     public TaskModel()
   5:     {
   6:         _storage = new Storage();
   7:     }
   8:     [Before("ActionsBeforeSave"), After("ActionsAfterSave")]
   9:     public void Save(Task task)
  10:     {
  11:         _storage.SaveOrUpdate(task);
  12:     }
  13:     public void ActionsBeforeSave()
  14:     {
  15:         //_storage.Connect();
  16:         Console.WriteLine("Action before save....");
  17:     }
  18:     public void ActionsAfterSave()
  19:     {
  20:         //_storage.Disconnect();
  21:         Console.WriteLine("Action after save....");
  22:     }
  23: }

Poi sulla classe Hook ho aggiunto un overload di Do che sfrutta l'uso dei gli attributi

   1: public class Hook
   2: {
   3:     public delegate void Action<A>(A a);
   4:  
   5:     public static void Do<A>(Action<A> action, A a)
   6:     {
   7:         Invoke(action, typeof(BeforeAttribute));
   8:         action(a);
   9:         Invoke(action, typeof(AfterAttribute));
  10:     }
  11:  
  12:     private static void Invoke(Delegate action, Type type)
  13:     {
  14:         object[] attributes = action.Method.GetCustomAttributes(type, true);
  15:         ActionAttribute attribute = (ActionAttribute)attributes[0];
  16:         MethodInfo method = action.Target.GetType().GetMethod(attribute.Action);
  17:         method.Invoke(action.Target, null);
  18:     }
  19: }

che poi nel viene usata così:

   1: Task task = new Task(1, DateTime.Today, "Demo task");
   2: IModel model = new TaskModel();
   3: Hook.Do(model.Save, task);

Per concludere l'utilizzo del metodo Do della classe Hook è un pò primitivo, andrebbe migliorato in modo che si possa invocare direttamente il metodo Save del model (come l'esempio all'iniziale) senza dove esplicitamente usare Hook.Do(...) . Non dovrebbe essere difficile, magari usando un Decorator, potrer migliorare anche questo aspetto, ma questo è un'altro post...

Technorati tags: , , ,

posted @ sabato 5 gennaio 2008 17:45

Print

Comments on this entry:

# re: Callbacks hook come RubyOnRails in C#

Left by Emanuele DelBono at 05/01/2008 19:24
Gravatar
Prova a dare un'occhiata anche agli Interceptor di Castle: con un pizzico di AOP ti permettono questo tipo di operazioni.

# Re: Callbacks hook come RubyOnRails in C#

Left by Alessandro Melchiori at 05/01/2008 19:39
Gravatar
Esatto...leggendo le prime righe del tuo post non può non venire in mente AOP.
Anche Sping.Net, con i suoi Aspect, JoinPoint, Advice (and so on...) ti mette a disposizione queste funzionalità.
E per agevolarti nella lettura...ecco il link:
http://www.springframework.net/docs/1.1/reference/html/aop.html

# re: Callbacks hook come RubyOnRails in C#

Left by makka at 05/01/2008 20:28
Gravatar
Certamente un framework di AOP (castle/spring.net) mi permette di fare queste e altre cose.
Volevo solo dimostrare come poterle fare anche senza dover usare un framewrok di terze parti, in fondo ho usato solo qlc classe.
Grazie comunque per il suggerimento!

# re: Callbacks hook come RubyOnRails in C#

Left by Emanuele DelBono at 06/01/2008 22:10
Gravatar
Beh il mio suggerimento era per invogliarti a guardare nei sorgenti di Castle per vedere come fanno loro e scrivere un bel post anche su quello ;-)

Comments have been closed on this topic.