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 >

Monday, February 20, 2012

MVC ListBox using EditorTemplates and KnockoutJS

Editor templates are really handy tool when we want to create controls for a specific type. Recently I had a requiremnt to show winform styled ListBox control with lists for Available/Selected and allowing user to move items from one collection to other and move items up/down in selected collection. It was similar to this:
Step 1: Considering this as a pretty common functionality in our application,I created a ListSource class to support this UI.
MvcLists\Models\ListSource.cs
  1. using System.Collections.Generic;
  2.  
  3. namespace MvcLists.Models
  4. {
  5.     public class ListSource
  6.     {
  7.         public List<Item> Available { get; set; }
  8.         public List<Item> Selected { get; set; }
  9.     }
  10.  
  11.     public class Item
  12.     {
  13.         public int Value { get; set; }
  14.         public string Text { get; set; }
  15.     }
  16. }
Step 2: Add a property of type ListSource to Model.
MvcLists\Models\Person.cs
  1. public class Person
  2.     {
  3.         [Tooltip("Enter First Name")]
  4.         public string FirstName { get; set; }
  5.         [Tooltip("Enter Last Name")]
  6.         public string LastName { get; set; }
  7.         [Tooltip("Enter SSN")]
  8.         [Mask("999-99-9999")]
  9.         public string SSN { get; set; }
  10.         [Tooltip("Enter Age")]
  11.         [Mask("99")]
  12.         public string Age { get; set; }
  13.         [Mask("(999)-999-9999")]
  14.         [Tooltip("Enter Phone")]
  15.         public string Phone { get; set; }
  16.         [Mask("99999?-9999")]
  17.         [Tooltip("Enter Zip Code")]
  18.         public string ZipCode { get; set; }
  19.         [Mask("9999-9999-9999-9999")]
  20.         [Tooltip("Enter Credit Card")]
  21.         public string CreaditCard { get; set; }
  22.         [AutoComplete("Person", "GetStates", "state")]
  23.         public string State { get; set; }
  24.         [AutoComplete("Person", "GetStates", "state")]
  25.         public string USState { get; set; }
  26.         public ListSource Country { get; set; }
  27.     }
Step 3: Created an EditorTemplate for this type and added knockoutJS functions for move items left/right/up/down.
MvcLists\Views\Shared\EditorTemplates\ListSource.cshtml
  1. @model MvcLists.Models.ListSource
  2. <style type="text/css">
  3.     .divItem
  4.     {
  5.         float: none;
  6.         border-bottom-width: .2em;
  7.         border-bottom-style: solid;
  8.         border-bottom-color: #000000;
  9.         padding: 5px;
  10.         cursor: pointer;
  11.     }
  12.     .rightBoxItem
  13.     {
  14.         padding-left: 10px;
  15.     }
  16.     .indexCell
  17.     {
  18.         /*border-right-width: .2em;
  19.             border-right-style: solid;
  20.             border-right-color: #900;*/
  21.         padding: 5px;
  22.         padding-right: 10px;
  23.     }
  24.     .selectedItem
  25.     {
  26.         background: gold;
  27.     }
  28.     .header
  29.     {
  30.         font-family: arial;
  31.         color: #003469;
  32.         background-color: #dcdcdc;
  33.         margin: 0 0 10px 0;
  34.         padding: 2px 5px 2px 5px;
  35.     }
  36. </style>
  37. <script src="../../../Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
  38. <script src="../../../Scripts/knockout-2.0.0.js" type="text/javascript"></script>
  39. <script>
  40.         
  41.         function ListViewModel() {
  42.             var listItem = function(text,id,itemIndex) {
  43.                 this.Text = text;
  44.                 this.Id = id;
  45.             };
  46.             var self = this;
  47.             var listA = @Html.Raw(Json.Encode(Model.Available.ToList()));
  48.             var listB = @Html.Raw(Json.Encode(Model.Selected.ToList()));
  49.             self.listAArray= ko.observableArray(ko.utils.arrayMap(listA,function(list) {
  50.                 return new listItem(list.Text, list.Value,index);
  51.             }));
  52.             var index = 0;
  53.             self.listBArray= ko.observableArray(ko.utils.arrayMap(listB,function(list) {
  54.                 index++;
  55.                 return new listItem(list.Text, list.Value,index);
  56.             }));
  57.             self.changeClass = function(item) {
  58.                 $(item).closest("divItem").toggleClass("selectedItem");
  59.             };
  60.             self.moveRight = function() {
  61.                 $(".leftBox .selectedItem").each(function() {
  62.                     var item=ko.dataFor(this);
  63.                     self.listBArray.push(item);
  64.                     self.listAArray.remove(item);
  65.                 });
  66.             };
  67.             self.moveLeft = function() {
  68.                 $(".rightBox .selectedItem").each(function() {
  69.                     var item = ko.dataFor(this);
  70.                     self.listAArray.push(item);
  71.                     self.listBArray.remove(item);
  72.                     
  73.                 });
  74.             };
  75.             self.moveUp = function() {
  76.                 $(".rightBox .selectedItem").each(function() {
  77.                     var item = ko.dataFor(this);
  78.                     var index = self.listBArray.indexOf(item);
  79.                     if(index>0) {
  80.                         self.listBArray.remove(item);
  81.                         self.listBArray.splice(index - 1, 0, item);
  82.                         $(".rightBox .divItem:eq(" + (index -1) + ")").toggleClass("selectedItem");
  83.                     } else {
  84.                         return false;  
  85.                     }
  86.  
  87.                 });
  88.             };
  89.             self.moveDown = function() {
  90.                 $(".rightBox .selectedItem").each(function() {
  91.                     var item = ko.dataFor(this);
  92.                     var index = self.listBArray.indexOf(item);
  93.                     if(index<self.listBArray.length-1) {
  94.                         self.listBArray.remove(item);
  95.                         self.listBArray.splice(index + 1, 0, item);
  96.                         $(".rightBox .divItem:eq(" + (index +1) + ")").toggleClass("selectedItem");
  97.                     } else {
  98.                         return false;  
  99.                     }
  100.  
  101.                 });
  102.             };
  103.             
  104.             self.afterRender = function() {
  105.                 var i = 0;
  106.                 $(".rightBox .divItem").each(function() {
  107.                     i++;
  108.                     $(this).find(".indexCell")[0].text(i);
  109.                 });
  110.             };
  111.             ko.bindingHandlers.Index = {
  112.                 update:function (element,valueAccessor) {
  113.                     var i = 0;
  114.                     $(".rightBox .indexCell").each(function() {
  115.                         i++;
  116.                         $(this).text(i);
  117.                     });
  118.                     $(".divItem").unbind("click").bind("click", function() {
  119.                         $(this).toggleClass("selectedItem");
  120.                     });
  121.                 }
  122.             };
  123.         }
  124.  
  125.         $(document).ready(function () {
  126.             ko.applyBindings(new ListViewModel());
  127.         });
  128. </script>
  129. @Html.LabelForModel()
  130. <div style="margin-left: 5px; width: 100%;">
  131.     <div style="margin: auto; width: 90%;">
  132.         <div>
  133.             <div style="float: left">
  134.                 <div>
  135.                     <span>Available</span>
  136.                 </div>
  137.                 <div data-bind="foreach:listAArray" style="color: black; background-color: #dcdcdc;
  138.                     display: inline-block; border-style: solid; border-width: 2px; border-bottom-width: 0px;
  139.                     border-color: #000000" class="leftBox">
  140.                     <div class="divItem">
  141.                         <span data-bind="text:Text"></span>
  142.                     </div>
  143.                 </div>
  144.             </div>
  145.             <div style="float: left; vertical-align: middle; padding: 5px; display: inline-block;
  146.                 margin-top: 50px; width: 75px;">
  147.                 <div>
  148.                     <div>
  149.                         <input type="button" data-bind="click:moveRight" value=">" /></div>
  150.                     <div>
  151.                         <input type="button" data-bind="click:moveLeft" value="<" /></div>
  152.                 </div>
  153.             </div>
  154.             <div style="float: left">
  155.                 <div>
  156.                     <span>Selected</span>
  157.                 </div>
  158.                 <div class="rightBox" data-bind="foreach:listBArray,Index:listBArray" style="
  159.                     background-color: #FFFFAA; display: inline-block; border-style: solid; border-width: 2px;
  160.                     border-color: #000000; border-bottom-width: 0px">
  161.                     <span class="indexCell" style="float: left;"></span>
  162.                     <div class="divItem rightBoxItem">
  163.                         <span data-bind="text:Text"></span>
  164.                     </div>
  165.                 </div>
  166.             </div>
  167.             <div style="float: left; vertical-align: middle; padding: 5px; display: inline-block;
  168.                 margin-top: 50px; width: 75px;">
  169.                 <div style="background-color: bisque;">
  170.                     <div>
  171.                         <input type="button" data-bind="click:moveUp" value="Move Up" /></div>
  172.                     <div>
  173.                         <input type="button" data-bind="click:moveDown" value="Move Down" /></div>
  174.                 </div>
  175.             </div>
  176.             <div style="clear: both">
  177.             </div>
  178.         </div>
  179.     </div>
  180. </div>
Step 4: In controller/viewModel set DataSource for list box.
MvcLists\Controllers\PersonController.cs
  1. public ListSource Countries
  2.         {
  3.             get
  4.             {
  5.                 return new ListSource
  6.                            {
  7.                                Available = new List<Item>
  8.                                                {
  9.                                                    new Item {Text = "US", Value = 1},
  10.                                                    new Item {Text = "UK", Value = 2},
  11.                                                    new Item {Text = "Canada", Value = 3},
  12.                                                    new Item {Text = "France", Value = 4}
  13.                                                },
  14.                                Selected = new List<Item> {new Item {Text = "India", Value = 5}}
  15.                            };
  16.             }
  17.         }
Step 5: Render this listbox(Country)
MvcLists\Views\Person\Index.cshtml
  1. @using MvcLists.Common.HtmlHelpers
  2. @model MvcLists.Models.Person
  3. @{
  4.     ViewBag.Title = "Index";
  5.     Layout = "~/Views/Shared/_Layout.cshtml";
  6. }
  7. <h2>
  8.     Index</h2>
  9. <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
  10. <script src="../../Scripts/jquery.maskedinput.js" type="text/javascript"></script>
  11. @using (Html.BeginForm("Index","Person"))
  12. {
  13.     <fieldset>
  14.         <legend>Person</legend>
  15.         <div class="editor-label">
  16.             @Html.LabelFor(model => model.FirstName)
  17.         </div>
  18.         <div class="editor-field">
  19.             @Html.TextBoxFor(model => model.FirstName,new{title=@Html.TooltipFor(x=>x.FirstName)})
  20.         </div>
  21.         <div class="editor-label">
  22.             @Html.LabelFor(model => model.LastName)
  23.         </div>
  24.         <div class="editor-field">
  25.             @Html.TextBoxFor(model => model.LastName,new{title=Html.TooltipFor(x=>x.LastName)})
  26.         </div>
  27.         @Html.EditorFor(x => x.SSN)
  28.         @Html.EditorFor(x => x.Age)
  29.         @Html.EditorFor(x => x.Phone)
  30.         @Html.EditorFor(x => x.CreaditCard)
  31.         @Html.EditorFor(x => x.ZipCode)
  32.         @Html.EditorFor(x=>x.State)
  33.         @Html.EditorFor(x=>x.USState)
  34.         @Html.EditorFor(x=>x.Country)
  35.  
  36.         <p>
  37.             <input type="submit" value="Create" />
  38.         </p>
  39.     </fieldset>
  40. }
  41. <div>
  42.     @Html.ActionLink("Back to List", "Index")
  43. </div>

Sunday, February 19, 2012

MVC Mask Attribute for Inputs

I created a mask input in previous posts where I declared Jquery as a constant and added it to Scripts collection. Alternate approach can be generate an Id for each mask input and use scripts in EditorTemplate.
MaskAttribute
  1. public void OnMetadataCreated(ModelMetadata metadata)
  2.         {
  3.             metadata.TemplateHint = "_maskInput";
  4.             metadata.AdditionalValues["id"] = Guid.NewGuid();
  5.             metadata.AdditionalValues["mask"] = Mask;
  6.         }
Register mask for input in document ready event.
Views\Shared\EditorTemplates\_maskInput.cshtml
  1. @using MvcLists.Common.CustomAttributes
  2. @model System.String
  3. @{
  4.     var additionalValues = ViewData.ModelMetadata.AdditionalValues;
  5.     var mask = additionalValues.SingleOrDefault(x=>x.Key=="mask").Value;
  6.     var id = additionalValues.SingleOrDefault(x => x.Key == "id").Value;
  7. }
  8. @{ var maskedInput = ViewData.GetModelAttribute<MaskAttribute>();
  9.    if (maskedInput != null)
  10.    {
  11.         <div class="editor-label">
  12.             @Html.LabelForModel()
  13.         </div>
  14.         <div class="editor-field">
  15.             @Html.TextBoxFor(m => m, new { id = @id })
  16.         </div>
  17.    }
  18. }
  19. <script type="text/javascript">
  20.     $(document).ready(function () {
  21.         $("#@id").mask('@mask');
  22.     });
  23. </script>