static void Main(string[] args) { Scope<LoggingFrame>.Wrap(() => { Console.WriteLine("Hello world"); }); }
Would produce this console
2012-03-07 00:46:58,248 DEBUG - BEGIN Main Hello world 2012-03-07 00:46:58,258 DEBUG - END Main
How to implement that ?
[DebuggerStepThrough] public class Scope<TAdvice> where TAdvice : IAdvice, new() { public static void Wrap(Action body) { IAdvice advice = Activator.CreateInstance<TAdvice>(); advice.OnEntry(body); try { body(); advice.OnLeave(body); } catch (Exception ex) { advice.OnException(body, ex); throw; } finally { advice.OnFinally(body); } } }
public interface IAdvice { void OnEntry(Delegate body); void OnLeave(Delegate body); void OnException(Delegate body, Exception exception); void OnFinally(Delegate body); }Implementation of the LoggingFrame advice is trivial.
Note that you could get similar behavior with IDisposable and using keyword, but you would not be able to log pending exception.
You could also think about TransactionScope, which would call tx.Complete() automatically when no exception is thrown.
Further improvement is to use dependency injection to instantiate the advises.
Another use-case is to implement Unit of Work or session/call context, while using TSL to reach topmost frame. I used it for NHibernate Session and EF DbContext (unit of work) recently. Interesting related article here.
private void Main(string[] args) { Scope<UnitOfWork>.Wrap(session => { var people = session.Person .Where(person => person.FirstName == "Pavel") .ToList(); NestedLogic(people); //DbContext.SaveChanges() will be called here }); } private void NestedLogic(IList<Person> people) { //this will lookup parent session in TSL and reuse it Scope<UnitOfWork>.Wrap(session => { foreach (var person in people) { if (person.LastName == "Savara") { person.Coder = true; } else { session.Person.Remove(person); } } }); }
Composition of scopes could be beautified.
Scope<LoggingFrame, TransactionFrame>.Wrap(() => { throw new Exception("rollback please"); }); [DebuggerStepThrough] public class Scope<TOuterAdvice, TInnerAdvice> where TOuterAdvice : IAdvice, new() where TInnerAdvice : IAdvice, new() { public static void Wrap(Action body) { Scope<TOuterAdvice>.Wrap(()=> Scope<TInnerAdvice>.Wrap(body)); } }
All code in the article is simplified and real implementation is exercise for readers.
Enjoy :-)
No comments:
Post a Comment