Introduction
In this installment of the series, we will talk about logging and exception handling. Let's say we build a public service and we have to communicate with a bunch of third-party services. It's obvious that we will have situations where we have to handle exceptions. In order to do that, we used to wrap potentially dangerous parts of code into try catch
expressions. What if we could have functionality to get all values of properties at the moment when exception occurs? It will be very important information for logging. On the other hand it’s hard, either we will copy paste the same code in every catch
expression which is very bad and hard to maintain or create some abstraction for it and stretching cross all code layers where it will be needed. These concerns often cannot be cleanly decomposed from the rest of the system in both the design and implementation, and can result in either scattering (code duplication), tangling (significant dependencies between systems), or both.
Background
This tip assumes that you're familiar with the concept of AOP and PostSharp framework. Also if you're wondering what the entire
KingAOP
Framework is about, click here.
Using the Code
Ok then, what can we do with AOP? Basically every AOP framework provides us with the ability to intercept exception throwing and handle it like we want. We can continue execution or wrap into another type of exception or just rethrow current exception or even drown exception. Actually our aspect should intercept exception, walk through all properties of instance and write the property's value into log file or elsewhere and finally just rethrow exception. In order to do that, we will inherit from OnMethodBoundaryAspect and override OnException method that will be called when exception occurs.
class ExceptionHandlingAspect : OnMethodBoundaryAspect
{
public override void OnException(MethodExecutionArgs args)
{
var str = new StringBuilder();
str.AppendLine();
str.Append(args.Exception.Message);
str.AppendLine();
if (args.Instance != null)
{
var instType = args.Instance.GetType();
str.AppendFormat("Type = {0}; ", instType.Name);
foreach (var property in instType.GetProperties(BindingFlags.Instance
| BindingFlags.Public | BindingFlags.DeclaredOnly))
{
str.AppendFormat("{0} = {1}; ", property.Name,
property.GetValue(args.Instance, null));
}
}
str.AppendLine();
Console.WriteLine(str.ToString());
args.FlowBehavior = FlowBehavior.RethrowException;
}
}
Since for this demo we have to communicate with a bunch of third-party services, let's create our fake public service.
class PublicService
{
public string SessionId { get; set; }
public string ServiceName { get; set; }
public uint Port { get; set; }
public void Send(TestEntity entity)
{
throw new Exception();
}
}
Also we have TestEntity
class which is our argument of Send
method of PublicService
class.
class TestEntity
{
public int Number { get; set; }
public string Name { get; set; }
}
In the current moment, we have all things which we need, let's combine them together. Basically we need that our PublicService
can be handled by our ExceptionHandlingAspect
aspect. In order to work with KingAOP, we have to inherit our PublicService
implementation from interface called IDynamicMetaObjectProvider. It's necessary for DLR to invoke our aspect via dynamic. You can think about it as indicator for DLR to do dynamic magic. The IDynamicMetaObjectProvider
requires to implement GetMediaObject method. This method should always returns KingAOP's AspectWeaver object which will generate code of aspect. The second step is binding our aspect on public
method Send
.
class PublicService : IDynamicMetaObjectProvider
{
public string SessionId { get; set; }
public string ServiceName { get; set; }
public uint Port { get; set; }
[ExceptionHandlingAspect]
public void Send(TestEntity entity)
{
throw new Exception();
}
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new AspectWeaver(parameter, this);
}
}
Let’s look at the whole demo:
class Program
{
static void Main()
{
var entity = new TestEntity { Name = "Jon", Number = 99 };
dynamic publicService = new PublicService
{
ServiceName = "TestService",
Port = 1234,
SessionId = "123908sdfhjkgdfg"
};
try
{
publicService.Send(entity);
}
catch (Exception ex)
{
Console.WriteLine("We catched original exception after 'exception aspect' did his job.");
}
Console.Read();
}
}
On console output, we will have: