There are many posts available to add AOP to the appliaction but as a starter it was very difficult to follow some of them. so just trying to
make an attempt to make this simpler. Recently I wanted to add logging ,auditing, exception handling to an existing appliaction and going by regular norms to add try / catch/ finally blocks for this could be cumbersome so I was searching for the better approach and came across this concept of AOP.
This is used when we want to implement a ceratin functionality across platforms in an application e.g. logging. so with this, we intercept the method callls wrap it with information we need to add and execute this.
The application I built is pretty straight forward calculator app.
Calculator
- using System;
-
- namespace AOP
- {
- public interface ICalculator
- {
- int Add(int i, int j);
- int Subtract(int i, int j);
- int Multiply(int i, int j);
- int Divide(int i, int j);
- }
-
- public class Calculator : ICalculator
- {
- public int Add(int i, int j)
- {
- var result = i + j;
- Console.WriteLine("result:{0}", result);
- return result;
- }
-
- public int Subtract(int i, int j)
- {
- var result = i - j;
- Console.WriteLine("result:{0}", result);
- return result;
- }
-
- public int Multiply(int i, int j)
- {
- var result = i * j;
- Console.WriteLine("result:{0}", result);
- return result;
- }
-
- public int Divide(int i, int j)
- {
- var result = i / j;
- Console.WriteLine("result: {0}", result);
- return result;
- }
- }
- }
We need to log information like
//Time : Method {MethodName} execution started
//Input parameters : {ParamterName} {ParameterValue}
// Output value : {result}
//Time taken for execution
To add this much information to each method, will make the code with difficult to read / manage and violate Single responsibility and DRY pronciples.
so to fix all this, we can intercept each method call of interface and let Castle Windsor create a proxy of this class to help with interception.
As I prefer StructureMap as IoC container, I am using EnrichWith feature of StrutureMap to link proxy from Castle Windsor to enrich Calculator functions with logging functionality.
Program.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using Castle.DynamicProxy;
- using StructureMap;
- using log4net;
-
- namespace AOP
- {
- class Program
- {
- static void Main(string[] args)
- {
- log4net.Config.XmlConfigurator.Configure();
- ObjectFactory.Initialize(x =>
- {
- x.Scan(y =>
- {
- y.TheCallingAssembly();
- y.WithDefaultConventions();
- });
- var proxy = new ProxyGenerator();
- x.For<ICalculator>().EnrichAllWith(
- p =>
- proxy.CreateInterfaceProxyWithTarget<ICalculator>(p,
- new LogInterceptor(
- LogManager.GetLogger(typeof(Program))
- )));
- });
-
-
-
- ObjectFactory.GetInstance<ICalculator>().Add(2, 3);
-
- ObjectFactory.GetInstance<ICalculator>().Subtract(3,2);
-
- ObjectFactory.GetInstance<ICalculator>().Multiply(2,3);
-
- ObjectFactory.GetInstance<ICalculator>().Divide(4,2);
-
- Console.ReadLine();
- }
- }
-
-
-
-
- }
and finally the LoggingInterceptor which implements IInterceptor from Catsle.DynamicProxy namespace.
LogInterceptor
- using System;
- using System.Linq;
- using Castle.DynamicProxy;
- using log4net;
-
- namespace AOP
- {
- public class LogInterceptor : IInterceptor
- {
- private readonly ILog _logger;
- public LogInterceptor(ILog logger)
- {
- _logger = logger;
- }
-
- public void Intercept(IInvocation invocation)
- {
- try
- {
- var parameters = invocation.Method.GetParameters().ToList();
- _logger.InfoFormat("Started Logging method: {0}", invocation.Method.Name);
- for (int i = 0; i < parameters.Count; i++)
- {
- _logger.InfoFormat(" Parameter[{2}] Name: {0} Value: {1}", parameters[i].Name, invocation.Arguments[i], i);
- }
-
- invocation.Proceed();
- _logger.InfoFormat("Logged Successfully return value: {0}", invocation.ReturnValue);
- _logger.Info("------------------------------------------------------------------------");
-
- }
- catch (Exception ex)
- {
- _logger.ErrorFormat("Exception occurred: {0}", ex.Message);
- }
- finally
- {
- _logger.Info("Logging Completed");
- }
-
- }
- }
- }
Log4Net Configuration:
-
<?
xml
version=
"1.0"
?>
-
<
configuration
>
-
<
configSections
>
-
<
section
name=
"log4net"
type=
"log4net.Config.Log4NetConfigurationSectionHandler,Log4net"
/>
-
</
configSections
>
-
<
startup
>
-
<
supportedRuntime
version=
"v4.0"
sku=
".NETFramework,Version=v4.0"
/>
-
</
startup
>
-
-
<
log4net
>
-
<
root
>
-
<
level
value=
"DEBUG"
/>
-
<
appender-ref
ref=
"LogFileAppender"
/>
-
</
root
>
-
<
appender
name=
"LogFileAppender"
type=
"log4net.Appender.RollingFileAppender"
>
-
<
param
name=
"File"
value=
"C:\logs\test\log.txt"
/>
-
<
param
name=
"AppendToFile"
value=
"true"
/>
-
<
maximumFileSize
value=
"10MB"
/>
-
<
staticLogFileName
value=
"true"
/>
-
<
layout
type=
"log4net.Layout.PatternLayout"
>
-
<
param
name=
"ConversionPattern"
value=
"%-5p%d{yyyy-MM-dd hh:mm:ss} – %m%n"
/>
-
</
layout
>
-
</
appender
>
-
</
log4net
>
-
</
configuration
>