Introduction
I've been making some WCF services recently and I am now at the point that a uniform exception handling would be needed. Of course, at first I thought of Enterprise Library. Its part is the Exception Handling Application Block, which is a very nice solution; however, I also do not like it because of the same reasons why I do not like Validation Application Block (too complex, hard to use). Or, there is Policy Injection Application Block. This is also a nice one, but you have to work too much to get any results.
If we think about it, these libs are nothing else but code, this code has a scope (validity range) and before and after it we want to inject code, into the same validity range.
EHAB makes a uniform exception code after our code according to certain rules, so we do not have to write handlers in each method. These can be held at one place and EHAB makes the rest.
PIAB will execute certain handlers given code before and after the execution of our code. E.g. validation and authorization before, exception handling and log after.
For this task, the aspect oriented programming was developed too. There are different libs for this too, lot of them are open source.
These are all difficult solutions for the same problem. According to the razor of Ockham, the most simple solution is the correct; that’s why I thought about how to solve this thing in simplest terms, in C# 3.0 for now and forever.
The Idea
There is a very useful construction in C#; there are too few who know how useful it could be in the hands of a coder. This is the anonymous method. Beginning from C# 3.0, the efficiency of this opportunity has increased, and can be entered with lambda syntax too. E.g.:
Func< int, bool > isPositive = v => v > 0;
This is a lambda expression, from which the compiler will generate anonym method, delegate
being its type. But there is also an opportunity to give code block:
Func< int, string > listOf = v =>
{
string result = null;
for (int i = 0; i < v; i++)
{
result = i == 0 ? i.ToString() : string.Format("{0}, {1}", result, i.ToString();
}
return result;
}
So, we do have a much shorter and more simple criss-cross instead of the long syntax:
delegate (string arg1, int arg2, ..., FooNs.FooClass argn) { ... }
Why is this anonym method so interesting? Let’s see how it works:
class MyClass
{
Func< int, bool > MyMethod(int arg1, int arg2)
{
Func< int, bool > anonim = ( argOfAninom) =>
{
return true.
}
return anonim;
}
}
Using anywhere, in any context, the anonym method in the example will access the context on the creation places 1, 2, and 3., especially it will stay part of the call-validity scope of MyMethod!
If we give some recursion to that, we have a scope list which is built on each other and managed totally by CLR. We can also use this for catching exceptions, being also the code stack valid through the whole call list!
The Solution
Knowing these, to implement a simple code injection is not a difficult task. We only need one structure:
public struct Scope
{
public delegate Chain Chain(Chain code);
public delegate void Block();
public Scope(params Chain[] codes)
{
this.code = null;
if (codes != null)
{
foreach (Chain code in codes)
{
AddCode(code);
}
}
}
public Scope(params Scope[] others)
{
this.code = null;
if (others != null)
{
foreach (Scope other in others)
{
AddCode(other.code);
}
}
}
Chain code;
private static Chain BlockToChain(Block code)
{
return c =>
{
code();
return null;
};
}
private void AddCode(Chain otherCode)
{
if (otherCode != null)
{
Chain thisCode = this.code;
if (thisCode == null)
{
this.code = otherCode;
}
else
{
this.code = (c) => otherCode(thisCode(c));
}
}
}
public Block Begin
{
set
{
if (value == null) throw new ArgumentNullException("value");
if (code != null)
{
code(BlockToChain(value))(null);
}
}
}
public static Scope operator +(Scope scope1, Scope scope2)
{
Scope result = new Scope(scope1);
result.AddCode(scope2.code);
return result;
}
public static Scope operator +(Scope scope, Chain code)
{
Scope result = new Scope(scope);
result.AddCode(code);
return result;
}
public static implicit operator Scope(Chain code)
{
return new Scope(code);
}
public static implicit operator Chain(Scope scope)
{
return scope.code;
}
}
The soul of it is a delegate
-construction which refers to itself. On that ground, we are able to create the anonym method calling link. This little structure can be considered as a compiler, it can be combined with other Scope
structures or Chain
handler methods (see the constructor and the operators).
Using the Code
It is very simple. Let’s see how to define exception handling scope
s. It is useful to classify the exceptions according to functions, to grade them into classes and to write handlers for these classes.
E.g. argument exception handling:
static Scope.Chain ArgumentExceptionScope(Scope.Chain upperNode)
{
return (chainNode) =>
{
try
{
upperNode(null);
}
catch (ArgumentNullException ex)
{
if (echo) Console.WriteLine("Argument '{0}' is null.", ex.ParamName);
}
catch (ArgumentException ex)
{
if (echo) Console.WriteLine("Argument '{0}' is invalid.", ex.ParamName);
}
return null;
};
}
Exception handler for operations error:
static Scope.Chain InvalidOpertaionExceptionScope(Scope.Chain upperNode)
{
return (chainNode) =>
{
try
{
upperNode(null);
}
catch (InvalidOperationException)
{
if (echo) Console.WriteLine("Invalid operation.");
}
return null;
};
}
General exception handler:
static Scope.Chain ExceptionScope(Scope.Chain upperNode)
{
return (chainNode) =>
{
try
{
upperNode(null);
}
catch (Exception ex)
{
if (echo) Console.WriteLine("Unhandled '{0}' exception: {1}",
ex.GetType().Name, ex.Message);
}
return null;
};
}
And so on. These are static
methods, but this is not a condition, they can also belong to a class-instance. The essence of the idea is that the upperNode(null);
means the code to be decorated and we can write any code to it in the handler (e.g. TransactionScope
), the thing will be injected here. The upperNode()
call parameter is of course null
and also the return of the anonym, because the chainNode
parameter and the return value are only ballasts, the essence is the calling of the anonym method.
The handler sees the environment where we have defined, that is why it is simple to create a logger built upon it:
public class Logger
{
public Logger(string name)
{
Enabled = true;
Name = name;
logScope = new Scope(Log);
}
public bool Enabled { get; set; }
public string Name { get; private set; }
Scope logScope;
public Scope LogScope
{
get { return logScope; }
}
Scope.Chain Log(Scope.Chain upperNode)
{
if (Enabled)
return DoLog(upperNode);
else
return ln => { upperNode(null); return null; };
}
private Scope.Chain DoLog(Scope.Chain upperNode)
{
return node =>
{
Console.WriteLine(">LOG --- Entering: {0} ---", Name);
try
{
upperNode(null);
}
catch (Exception ex)
{
Console.WriteLine(">LOG --- Exception caught: {0} ---",
ex.GetType().Name);
throw ex;
}
Console.WriteLine(">LOG --- Leaving: {0} ---", Name);
return null;
};
}
}
All right, we have a lot of handlers which can be created and are running simply; how to use those? Simply: let’s build up a (or more) Scope
structure with operators:
Scope scope = new Scope();
scope += logger.LogScope;
scope += ArgumentExceptionScope;
scope += InvalidOpertaionExceptionScope;
scope += ExceptionScope;
Or with the constructor:
scope = new Scope(logger.LogScope,
new Scope(ArgumentExceptionScope, InvalidOpertaionExceptionScope),
ExceptionScope);
The scope
builds up from inside to outside, so the handler given at last will circle the whole with its code. It does not matter if we build this from a ready or complex scope or from a handler, it will run and the “bubble”-rule from inside to outside will be valid.
It is very flexible, because some parts of it can be driven from that context where we have created them. In this example, logging can be switched on or off via the logger object in those scope
s where this instance is there (logger Enabled). But we can drive the handlers with every kind of complex business rule. It is much more flexible than AOP and EntLib, because these are own systems, no kind of interfaces need to be implemented, nothing will be wired before and the CLR will arrange everything for us.
Nothing left but to run our code in a given scope
(to inject). Nothing is more easy:
myCoolScope.Begin = () =>
{
throw new Exception("Something went really wrong out there!");
};
We can do it in any method, the code will be injected into the environment defined by myCoolScope
, can be logged, exceptions will be handled, policy handling can be defined and so on. It can be seen from the code of Scope
that the code after Begin
will be executed at once (property set): this scope
after Begin
construction has to be imagined like a normal using block!
There is one difference to using: in the Scope Begin
code, you cannot simply return from the method with “return”, but this is also not a big problem. It is enough to keep up a local variable for this purpose:
private static void GetSomeValueTest()
{
Console.WriteLine("\nGetSomeValueTest()");
int v = 0;
Console.WriteLine("Value is {0}.", v);
v = GetSomeValueFromInjectedCode(v);
Console.WriteLine("Value is {0}.", v);
}
private static int GetSomeValueFromInjectedCode(int fromValue)
{
fullScope.Begin = () =>
{
fromValue = fromValue + 1978;
};
return fromValue;
}
The HandlerBase
I have also drawn a handler abstract
base, to make life easier:
public sealed class ScopeExceptionEventArg : EventArgs
{
public Exception Exception { get; internal set; }
public bool Handled { get; set; }
}
public abstract class HandlerBase
{
public HandlerBase()
{
scope = new Scope(ScopeMethod);
Enabled = true;
}
Scope scope;
public Scope Scope
{
get { return scope; }
}
public static implicit operator Scope(HandlerBase handler)
{
if (handler == null) return new Scope();
return handler.scope;
}
bool inFlag;
public bool Enabled { get; set; }
public event EventHandler EnterScope;
public event EventHandler LeaveScope;
public event EventHandler< ScopeExceptionEventArg > ScopeException;
Scope.Chain ScopeMethod(Scope.Chain code)
{
if (inFlag || !Enabled) return (p) => { code(null); return null; };
return (p) =>
{
inFlag = true;
try
{
OnEnterScope(EventArgs.Empty);
this.code = code;
Call();
}
finally
{
OnLeaveScope(EventArgs.Empty);
inFlag = false;
}
return null;
};
}
Scope.Chain code;
protected virtual void Call()
{
if (code != null)
{
try
{
code(null);
}
catch (Exception ex)
{
ScopeExceptionEventArg e = new ScopeExceptionEventArg { Exception = ex };
OnScopeException(e);
if (!e.Handled) throw ex;
}
finally
{
code = null;
}
}
}
protected virtual void OnEnterScope(EventArgs e)
{
EventHandler handler = EnterScope;
if (handler != null) handler(this, e);
}
protected virtual void OnLeaveScope(EventArgs e)
{
EventHandler handler = LeaveScope;
if (handler != null) handler(this, e);
}
protected virtual void OnScopeException(ScopeExceptionEventArg e)
{
EventHandler< ScopeExceptionEventArg > handler = ScopeException;
if (handler != null) handler(this, e);
}
}
This way we can create a logger (or anything else) much easier:
public class LoggerScope : HandlerBase
{
public LoggerScope() : base() { }
public string Message { get; set; }
protected override void OnEnterScope(EventArgs e)
{
base.OnEnterScope(e);
if (!string.IsNullOrEmpty(Message))
{
Console.WriteLine(">LOG --- Entering: {0} ---", Message);
}
}
protected override void OnLeaveScope(EventArgs e)
{
base.OnLeaveScope(e);
if (!string.IsNullOrEmpty(Message))
{
Console.WriteLine(">LOG --- Leaving: {0} ---", Message);
Message = null;
}
}
protected override void OnScopeException(ScopeExceptionEventArg e)
{
base.OnScopeException(e);
if (!string.IsNullOrEmpty(Message))
{
Console.WriteLine(">LOG --- Exception caught: {0},
Type: {1} ---", Message, e.Exception.GetType().Name);
}
}
}
Or a profiler:
public class ProfilerScope : HandlerBase
{
DateTime start;
public TimeSpan EllapsedTime
{
get;
private set;
}
protected override void OnEnterScope(EventArgs e)
{
base.OnEnterScope(e);
start = DateTime.Now;
EllapsedTime = TimeSpan.Zero;
}
protected override void OnLeaveScope(EventArgs e)
{
EllapsedTime = DateTime.Now - start;
base.OnLeaveScope(e);
}
}
And you can build a scope
from those:
LoggerScope logger = new LoggerScope();
ProfilerScope profiler = new ProfilerScope();
Scope scope = new Scope(profiler.Scope, logger.Scope);
scope.Begin = () =>
{
}
logger.Enabled = false;
scope.Begin = () =>
{
}
I have attached a test project so you can see how easy and efficient this is. In the test program, the exception handling is not much slower with this method than with ad hoc handler writing (it's about delegate
calls), but much more faster and simpler than EHAB.
A Real Life Example
To show an example: I have an entity manager service. In its business logic, exceptions can happen. E.g. we want to move a city to a region, which belongs to another country than the city was – this creates an exception, because it is wrong, this cannot be done. These exceptions are defined by the business logic, these are standard CLR exceptions. But, these do not cross the SOAP communication, these have to be converted to compatible SOAP fault messages. The woodman-method would be if I slapped catching and converting the exceptions into the code of the façade. This is not good, it would be the task of the business logic to do this – it is its competence. Maybe I could write a WCF handler which executes this work automatically, but the problem with that would be that I could not test the libs without WCF, statically attached.
So I have written a Scope
handler to the business logic, which from the façade can create the proper scope
:
public class DataExceptionConvertHandler : HandlerBase
{
protected override void Call()
{
try
{
base.Call();
}
catch (NameStoreItemNotFoundExcepton ex)
{
throw new FaultException< NameStoreItemNotFoundFault >
(new NameStoreItemNotFoundFault { NameStoreItemID = ex.ID }, ex.Message);
}
catch (NameStoreItemIsUnremovableExcepton ex)
{
throw new FaultException< NameStoreItemIsUnremovableFault >
(new NameStoreItemIsUnremovableFault
{ NameStoreItemID = ex.ID }, ex.Message);
}
catch (RegionNotFoundExcepton ex)
{
throw new FaultException< RegionNotFoundFault >
(new RegionNotFoundFault { RegionUID = ex.EnitityUID,
CountryCode = GeoUtils.GetCountryCodeOfLCID(ex.LCID) }, ex.Message);
}
catch (RegionIsUnremovableExcepton ex)
{
throw new FaultException< RegionIsUnremovableFault >
(new RegionIsUnremovableFault { RegionUID = ex.EnitityUID });
}
catch (SettlementNotFoundException ex)
{
throw new FaultException< SettlementNotFoundFault >
(new SettlementNotFoundFault { SettlementUID = ex.EnitityUID });
}
catch (SettlementIsUnremovableException ex)
{
throw new FaultException< SettlementIsUnremovableFault >
(new SettlementIsUnremovableFault { SettlementUID = ex.EnitityUID });
}
}
}
Everyone is in the right place, it is transparent, fast and easy to maintain. And this is how the service-façade looks like (now only this converting scope
is available in this example):
public partial class PartnerManagementService : IPartnerManagement
{
public PartnerManagementService()
{
facadeScope = new DataExceptionConvertHandler();
}
#region Scopes
Scope facadeScope;
#endregion
#region Validation
private static void ValidateWithNeutralLocale(object request)
{
}
private static void Validate(object request)
{
}
#endregion
}
public partial class PartnerManagementService
{
public NACENamesResponse GetNACENames(NACENamesRequest request)
{
NACENamesResponse response = null;
facadeScope.Begin = () =>
{
ValidateWithNeutralLocale(request);
INACEManager man = DataManagerFactory.CreateNACEManager();
List< NACENameModel > list = man.GetByLCID
(request.International.GetNeutralCulture().LCID);
response = new NACENamesResponse
{
SectorNames = list.ToArray()
};
};
return response;
}
public NACENameResponse GetNACEName(NACENameRequest request)
{
NACENameResponse response = null;
facadeScope.Begin = () =>
{
ValidateWithNeutralLocale(request);
INACEManager man = DataManagerFactory.CreateNACEManager();
response = new NACENameResponse
{
NACENameValue = man.GetName(request.NACECode,
request.International.GetNeutralCulture().LCID)
};
};
return response;
}
public void DefineNACEName(DefineNACENameRequest request)
{
facadeScope.Begin = () =>
{
ValidateWithNeutralLocale(request);
INACEManager man = DataManagerFactory.CreateNACEManager();
man.DefineName(request.NACEName,
request.International.GetNeutralCulture().LCID);
};
}
public void UndefineNACEName(UndefineNACENameRequest request)
{
facadeScope.Begin = () =>
{
ValidateWithNeutralLocale(request);
INACEManager man = DataManagerFactory.CreateNACEManager();
man.UndefineName(request.NACECode,
request.International.GetNeutralCulture().LCID);
};
}
}
So, this is how the poor man's code injector looks like in C# 3.0. Use it in good health!
History
- 18. Dec. 2007 - First version