Thursday, May 10, 2012

Automapper Ignore All Non Existing Properties

Code Snippet
  1. public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>
  2.                                             (this IMappingExpression<TSource, TDestination> expression)
  3.         {
  4.             var sourceType = typeof(TSource);
  5.             var destinationType = typeof(TDestination);
  6.             var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType.Equals(sourceType) && x.DestinationType.Equals(destinationType));
  7.             foreach (var property in existingMaps.GetUnmappedPropertyNames())
  8.             {
  9.                 expression.ForMember(property, opt => opt.Ignore());
  10.             }
  11.             return expression;
  12.         }

Monday, May 7, 2012

Aspect Oriented Programming (AOP)

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
  1. using System;
  2.  
  3. namespace AOP
  4. {
  5.     public interface ICalculator
  6.     {
  7.         int Add(int i, int j);
  8.         int Subtract(int i, int j);
  9.         int Multiply(int i, int j);
  10.         int Divide(int i, int j);
  11.     }
  12.  
  13.     public class Calculator : ICalculator
  14.     {
  15.         public int Add(int i, int j)
  16.         {
  17.             var result = i + j;
  18.             Console.WriteLine("result:{0}", result);
  19.             return result;
  20.         }
  21.  
  22.         public int Subtract(int i, int j)
  23.         {
  24.             var result = i - j;
  25.             Console.WriteLine("result:{0}", result);
  26.             return result;
  27.         }
  28.  
  29.         public int Multiply(int i, int j)
  30.         {
  31.             var result = i * j;
  32.             Console.WriteLine("result:{0}", result);
  33.             return result;
  34.         }
  35.  
  36.         public int Divide(int i, int j)
  37.         {
  38.             var result = i / j;
  39.             Console.WriteLine("result: {0}", result);
  40.             return result;
  41.         }
  42.     }
  43. }
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
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Castle.DynamicProxy;
  6. using StructureMap;
  7. using log4net;
  8.  
  9. namespace AOP
  10. {
  11.     class Program
  12.     {
  13.         static void Main(string[] args)
  14.         {
  15.             log4net.Config.XmlConfigurator.Configure();
  16.             ObjectFactory.Initialize(x =>
  17.                                          {
  18.                                              x.Scan(y =>
  19.                                                         {
  20.                                                             y.TheCallingAssembly();
  21.                                                             y.WithDefaultConventions();
  22.                                                         });
  23.                                              var proxy = new ProxyGenerator();
  24.                                              x.For<ICalculator>().EnrichAllWith(
  25.                                                  p =>
  26.                                                  proxy.CreateInterfaceProxyWithTarget<ICalculator>(p,
  27.                                                                                                    new LogInterceptor(
  28.                                                                                                        LogManager.GetLogger(typeof(Program))
  29.                                                                                                        )));
  30.                                          });
  31.  
  32.             
  33.  
  34.             ObjectFactory.GetInstance<ICalculator>().Add(2, 3);
  35.  
  36.             ObjectFactory.GetInstance<ICalculator>().Subtract(3,2);
  37.  
  38.             ObjectFactory.GetInstance<ICalculator>().Multiply(2,3);
  39.  
  40.             ObjectFactory.GetInstance<ICalculator>().Divide(4,2);
  41.  
  42.             Console.ReadLine();
  43.         }
  44.     }
  45.  
  46.     
  47.  
  48.     
  49. }
and finally the LoggingInterceptor which implements IInterceptor from Catsle.DynamicProxy namespace.
LogInterceptor
  1. using System;
  2. using System.Linq;
  3. using Castle.DynamicProxy;
  4. using log4net;
  5.  
  6. namespace AOP
  7. {
  8.     public class LogInterceptor : IInterceptor
  9.     {
  10.         private readonly ILog _logger;
  11.         public LogInterceptor(ILog logger)
  12.         {
  13.             _logger = logger;
  14.         }
  15.  
  16.         public void Intercept(IInvocation invocation)
  17.         {
  18.             try
  19.             {
  20.                 var parameters = invocation.Method.GetParameters().ToList();
  21.                 _logger.InfoFormat("Started Logging method: {0}", invocation.Method.Name);
  22.                 for (int i = 0; i < parameters.Count; i++)
  23.                 {
  24.                     _logger.InfoFormat(" Parameter[{2}] Name: {0} Value: {1}", parameters[i].Name, invocation.Arguments[i], i);
  25.                 }
  26.  
  27.                 invocation.Proceed();
  28.                 _logger.InfoFormat("Logged Successfully return value: {0}", invocation.ReturnValue);
  29.                 _logger.Info("------------------------------------------------------------------------");
  30.                 
  31.             }
  32.             catch (Exception ex)
  33.             {
  34.                _logger.ErrorFormat("Exception occurred: {0}", ex.Message);
  35.             }
  36.             finally
  37.             {
  38.                 _logger.Info("Logging Completed");
  39.             }
  40.  
  41.         }
  42.     }
  43. }
Log4Net Configuration:
  1. <? xml version= "1.0" ?>
  2. < configuration >
  3.   < configSections >
  4.     < section name= "log4net" type= "log4net.Config.Log4NetConfigurationSectionHandler,Log4net" />
  5.   </ configSections >
  6.   < startup >
  7.     < supportedRuntime version= "v4.0" sku= ".NETFramework,Version=v4.0" />
  8.   </ startup >
  9.   < log4net >
  10. < root >
  11. < level value= "DEBUG" />
  12. < appender-ref ref= "LogFileAppender" />
  13. </ root >
  14. < appender name= "LogFileAppender" type= "log4net.Appender.RollingFileAppender" >
  15. < param name= "File" value= "C:\logs\test\log.txt" />
  16. < param name= "AppendToFile" value= "true" />
  17. < maximumFileSize value= "10MB" />
  18. < staticLogFileName value= "true" />
  19. < layout type= "log4net.Layout.PatternLayout" >
  20. < param name= "ConversionPattern" value= "%-5p%d{yyyy-MM-dd hh:mm:ss} – %m%n" />
  21. </ layout >
  22. </ appender >
  23. </ log4net >
  24. </ configuration >