Mapper Extensions 1.0

This is documentation for the 1.0 release of Mapper Extensions. For the latest release, which features a fluid API, see the latest documentation.

Summary

Mapper Extensions is a simple object mapper implemented as a set of extension methods. Its goal is to map properties that belong to one object to another object, such as one might need to facilitate the use of Data Transfer Objects (DTOs).

Mapper Extensions accomplishes the following:
  • Automatically maps properties that share the same name in both objects
  • Mismatched names can be specified to allow mapping of properties that are not the same in the source and destination object
  • Trigger rules allow you to change data based on property name or data type
  • Properties that exist in the source object, but not the destination object are simply not mapped
  • Implemented as a set of extension methods
  • Properties can be explicitly excluded from mapping
  • Exclusion lists can use simple wildcards to exclude a whole set of properties (useful in some ORM scenarios)
  • Map one enumeration to another enumeration
  • Map DataTable to List<T>

Code Samples

The next few code samples will use the following simple classes to test the extension methods:

    public class Employee
    {
        public int EmployeeId { get; set;}
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime HireDate { get; set; }
        public DateTime? TerminationDate { get; set; }

    }

    public class EmployeeDTO
    {
        public int EmployeeId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime HireDate { get; set; }
        public DateTime? TerminationDate { get; set; }

        public EmployeeDTO()
        {
            
        }

        public EmployeeDTO(int employeeId, string firstName, string lastName, DateTime hireDate)
        {
            EmployeeId = employeeId;
            FirstName = firstName;
            LastName = lastName;
            HireDate = hireDate;
        }
   }

Simple Mapping

Copies values from source object to destination project if the property name matches

            Employee emp = new Employee
                               {
                                   EmployeeId = 1,
                                   FirstName = "John",
                                   LastName = "Smith",
                                   HireDate = DateTime.Parse("8/1/2009")
                               };

            EmployeeDTO dto = emp.MapProperties <EmployeeDTO>();


In this example all of the properties that exist in both the source object and destination object get copied. Note that the "Notes" property in the source does not get copied because it does not exist in the destination.

Here's a similar example:

            EmployeeDTO dto = new EmployeeDTO(0,String.Empty,String.Empty,DateTime.MinValue);

            dto = emp.MapProperties(dto);


The key difference here was that the destination object (dto) was passed in. In this example we did not need to do this because a parameterless constructor (default constructor) was available. If one had not been available, we would be forced to use this second approach.

Data Transformation Based On Type

Apply a data transformation before it gets copied from the source to the destination, based on the data type of the property

There are two ways to perform data transformations based on type. The first to be demonstrated can be used if there is only one type we wish to use to trigger a transformation. In this example, we only care about strings, where we wish to uppercase the string before writing the value into the destination.

            var dto = emp.MapProperties<EmployeeDTO>(typeof (string), s => s.ToString().ToUpper(), null, null);


The first parameter specifies which data type will trigger a transformation. The next parameter specifies what transformation should take place. The third parameter is used for aliased properties (mismatched names between source and destination), which will be discussed later. The final parameter is used for properties on the destination object that should not be mapped. This will also be discussed later.


Apply multiple data transformations based on types before writing the source values to the destination

If multiple transformations based on type are to be applied to the destination then we need to use this next approach.

            var dto =
                emp.MapProperties<EmployeeDTO>(
                    new DataTypeTriggers
                        {
                            {typeof (string), s => s.ToString().ToUpper()},
                            {typeof (DateTime?), d => d ?? DateTime.MaxValue}
                        }, null, null);


This example is very similar to the last example. Here, we provide DataTypeTriggers, a dictionary of types and transformations. For each type we give, we provide a transformation for that type. In this example we upper case all strings (just like the last example) and for any nullable DateTimes that are null we fill it with DateTime.Max instead.

Data Transformation Based On Property Name

Apply a transformation to the destination property if the source's property matches a specified transformation rule

In some cases we do not want to transform data based on type, but rather based on a set of properties. In the example below we do not want to transform all integers, only EmployeeId, to which we will add 10000.

            var dto =
                emp.MapProperties<EmployeeDTO>(new PropertyNameTriggers {{"EmployeeId", i => (int)i + 10000}}, null, null);

Aliased Names

A destination object need not have the same property names as the source object. If names do not match then a list of aliases must be provided to the mapping extension method

To demonstrate this feature we will modify the DTO that we've been using:

    public class EmployeeDTO
    {

        public int ID { get; set; }
        public string FName { get; set; }
        public string LName { get; set; }
    }


Now we'll perform a simple mapping between Employee and our new version of EmployeeDTO:

            var dto =
                emp.MapProperties<EmployeeDTO>(new Aliases
                                                   {{"ID", "EmployeeId"}, 
                                                   {"FName", "FirstName"}, 
                                                   {"LName", "LastName"}});


Here we provide the Aliases class; a dictionary of string mappings so that the mapper knows which fields are aliased. The dictionary key is the destination property name.

Type Conversion

Property types between source object and destination object do not have to match

Property types between source and destination do not have to match. The below example high-lites this by demonstrating a conversion from a string property in the source object to date property in the destination object.

    class Source
    {
        public string Date { get; set; }
    }

    class Destination
    {
        public DateTime Date { get; set; }
    }

    private static void Main(string[] args)
    {
            var s = new Source {Date = DateTime.Now.ToString()};
            var d = s.MapProperties<Destination>(
                new PropertyNameTriggers
                    {
                        {"Date", t => DateTime.Parse(t.ToString())}
                    }, 
                null);
    }


Exclusions

Your destination object might contain properties that you do not want mapped. To exclude properties from being mapped, include them in the Exclusions list. Below is an example:

            var dto = new EmployeeDTO();
            emp.MapProperties(dto,new Exclusions { "FirstName" });



In this example the "FirstName" property in the emp object is not mapped to "FirstName" in the dto object.

Exclusions also allow for simple wildcards. This is useful in some ORM scenarios, particularly if the ORM adds "baggage" to your class (e.g. Entity Framework 1.0 adds "Reference" objects to your class. If you want to map from one EF object to another EF object, you may not want to map the Reference objects. The wildcard allows you to eliminate all Reference Object mappings by including "*Reference" in the exclusion list). Below is an example:

            var dto = new EmployeeDTO();
            emp.MapProperties(dto,new Exclusions { "*Name" });


In this example, any property in the destination that ends with "Name" will not be mapped.

Map Enumeration To Enumeration

Mark G William (http://www.codeplex.com/site/users/view/markgwilliam) submitted a wrapper to map enumerations to new enumerations via extension method, e.g.

            var employees = new List<Employee>{ \\lots of objects here}
            var employeeDTOs = employees .MapGroup<EmployeeDTO>();


Version 1.0 adds all of the features to Enumeration To Enumeration mapping - Aliasing, Property Name Triggers, Data Type Triggers and Exclusions.

Assuming the following classes:

    public class Employee
    {
        public int EmployeeId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime HireDate { get; set; }
        public DateTime? TerminationDate { get; set; }
        public string Comments { get; set; }

    }

    public class EmployeeDTO
    {
        public string EmpId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime HireDate { get; set; }
        public DateTime? TerminationDate { get; set; }
        public string Comments { get; set; }
    }

The example below :
  • Aliases employee Id
  • Left pads employee Id with 0s (and converts to string)
  • Excludes the "Comments" field from mapping

            var DTOs =
                employees.MapGroup<EmployeeDTO>(
                    new PropertyNameTriggers {{"EmployeeId", id => id.ToString().PadLeft(6, '0')}},
                    new Aliases { { "EmpId", "EmployeeId" } }, new Exclusions { "Comments" });


Note: To use enumeration to enumeration mapping, the target class must have a parameterless constructor (default constructor). If this is not an option, you can always map using object to object mapping in a loop, adding each target object to a list.

Map DataTable to List<T>

Maps ADO.net DataTables to List<T>

Mapper Extensions can map DataTables to object lists, similarly to how it maps objects to objects.

Example mapping a DataTable with Aliases:

     var list = myDataTable.MapToObjectList<TestClass>(new Aliases {{"MyString", "MYSTRING"}}, null );
 

The above aliases "MYSTRING" in the DataTable with "MyString" in the object.

Example mapping a DataTable with an Alias and Data Trigger

   var list = myDataTable.MapToObjectList<TestClass>(new DataTypeTriggers{{typeof(int), i=>(int)i+1000}},new Aliases { { "MyString", "MYSTRING" } }, null);

The above aliases "MYSTRING" in the DataTable with "MyString" in the object and adds 1000 to all integers encountered in the DataTable.

Example mapping a DataTable with an Alias and Property Name Trigger
    var list = myDataTable.MapToObjectList<TestClass>(new PropertyNameTriggers() { { "MYSTRING", s => (string)s + "!!!" }, {"MyInt2", i=>(int)i+1000} }, new Aliases { { "MyString", "MYSTRING" } }, null);

The above aliases "MYSTRING" in the DataTable with "MyString" in the object, appends "!!!" to "MyString" and adds 1000 to "MyInt2".

Last edited Apr 13, 2011 at 6:38 AM by ggalbo, version 5

Comments

No comments yet.