TinyMapper介绍

原文:http://www.codeproject.com/Articles/886420/TinyMapper-yet-another-object-to-object-mapper-for

类似文章(Simple Model/Entity Mapper in C#) http://www.codeproject.com/Tips/807820/Simple-Model-Entity-mapper-in-Csharp,修改版本http://www.codeproject.com/Tips/885770/Simple-Model-Entity-Mapper-in-Csharp

核心代码如下:

public static class MapperUtility
{
    /*passing values to given object*/
    public static TTarget MapTo<TSource, TTarget>(this TSource aSource, TTarget aTarget)
    {
        const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic;
       
        /*TODO: find fields*/
        var srcFields = (from PropertyInfo aProp in typeof(TSource).GetProperties(flags)
                           where aProp.CanRead     //check if prop is readable
                             select new
                             {
                                 Name = aProp.Name,
                                 Type = Nullable.GetUnderlyingType(aProp.PropertyType) ?? aProp.PropertyType
                             }).ToList();
        var trgFields = (from PropertyInfo aProp in aTarget.GetType().GetProperties(flags)
                             where aProp.CanWrite   //check if prop is writeable
                                 select new
                                 {
                                     Name = aProp.Name,
                                     Type = Nullable.GetUnderlyingType(aProp.PropertyType) ?? aProp.PropertyType
                                 }).ToList();

        /*TODO: common fields where name and type same*/
        var commonFields = srcFields.Intersect(trgFields).ToList();

        /*assign values*/
        foreach (var aField in commonFields)
        {
            var value = aSource.GetType().GetProperty(aField.Name).GetValue(aSource, null);
            PropertyInfo propertyInfos = aTarget.GetType().GetProperty(aField.Name);
            propertyInfos.SetValue(aTarget, value, null);
        }
        return aTarget;
    }

    /*returns new object with mapping*/
    public static TTarget CreateMapped<TSource, TTarget>(this TSource aSource) where TTarget : new()
    {
        return aSource.MapTo(new TTarget());
    }
}
Student source = new Student() { Id = 1, Name = "Smith" };
/*get new object, which been mapped*/
StudentLog newMapped = source.CreateMapped<Student, StudentLog>();
/*mapp to existing object*/
StudentLog aSourceLog = new StudentLog();
Student target = new Student();
target.Id = 2;
target.Name = "Tom";

aSourceLog = target.MapTo(aSourceLog);
//or
target.MapTo(aSourceLog);




TinyMapper is an object to object mapper for .Net. The main advantage is performance. TinyMapper allows easily map object to object, i.e. properties or fields from one object to another, for instance.

private static void MapPerson()
{
    TinyMapper.Bind<Person, PersonDto>();

    var person = new Person
    {
        Id = Guid.NewGuid(),
        FirstName = "John",
        LastName = "Doe",
        Email = "[email protected]",
        Address = "Wall Street",
        CreateTime = DateTime.Now,
        Nickname = "Object Mapper",
        Phone = "Call Me Maybe "
    };

    var personDto = TinyMapper.Map<PersonDto>(person);
}

Performance

Yes, this chapter has to be on the end. I believe most of you know what is object mappers, so let's talk about performance briefly.

We'll use the same Person class for testing purpose.

public sealed class Person
{
    public string Address { get; set; }
    public string Email { get; set; }
    public string FirstName { get; set; }
    public Guid Id { get; set; }
    public string LastName { get; set; }
    public string Nickname { get; set; }
    public DateTime CreateTime { get; set; }
    public string Phone { get; set; }
}

PersonDto class is the same as Person. We'll compare:

  • Handwritten code
  • AutoMapper version 3.3.1
  • TinyMapper version 1.0.2

During test, we create a Persoclass instance and map all properties on the PersonDto class

private static void MeasureHandwritten()
{
    Person person = CreatePerson();

    Stopwatch stopwatch = Stopwatch.StartNew();

    for (int i = 0; i < Iterations; i++)
    {
        PersonDto personDto = MapHandwritten(person);
    }
    stopwatch.Stop();
    Console.WriteLine("Handwritten: {0}ms", stopwatch.Elapsed.TotalMilliseconds);
}

private static void MeasureTinyMapper()
{
    Person person = CreatePerson();
    TinyMapper.Bind<Person, PersonDto>();

    Stopwatch stopwatch = Stopwatch.StartNew();

    for (int i = 0; i < Iterations; i++)
    {
        var personDto = TinyMapper.Map<PersonDto>(person);
    }

    stopwatch.Stop();
    Console.WriteLine("TinyMapper: {0}ms", stopwatch.Elapsed.TotalMilliseconds);
}

private static void MeasureAutoMapper()
{
    Person person = CreatePerson();
    Mapper.CreateMap<Person, PersonDto>();

    Stopwatch stopwatch = Stopwatch.StartNew();

    for (int i = 0; i < Iterations; i++)
    {
        var personDto = Mapper.Map<PersonDto>(person);
    }
    stopwatch.Stop();
    Console.WriteLine("AutoMapper: {0}ms", stopwatch.Elapsed.TotalMilliseconds);
}

where CreatePerson

private static Person CreatePerson()
{
    return new Person
    {
        Id = Guid.NewGuid(),
        FirstName = "John",
        LastName = "Doe",
        Email = "[email protected]",
        Address = "Wall Street",
        CreateTime = DateTime.Now,
        Nickname = "Object Mapper",
        Phone = "Call Me Maybe "
    };
}

and MapHandwritten

private static PersonDto MapHandwritten(Person person)
{
    var result = new PersonDto
    {
        Id = person.Id,
        FirstName = person.FirstName,
        LastName = person.LastName,
        Email = person.Email,
        Address = person.Address,
        CreateTime = person.CreateTime,
        Nickname = person.Nickname,
        Phone = person.Phone
    };
    return result;
}

we repeat measurement 1 000 000 times.

Getting started

TinyMapper is available thru nugen, so you can install as all nuget package. for example thru Package Manager Console.

Okay, we're ready to start. First of all you've to Bind source type to target type, like this.

TinyMapper.Bind<Person, PersonDto>();

Now TinyMapper knows how to map Person object to PersonDto object.  Finally, call 

var personDto = TinyMapper.Map<PersonDto>(person);

and you'll get mapped object. Here's the full version.

So you have to do two steps:

  • Bind source type to target type
  • Map source object to target type

TinyMapper can do first step for you, i.e. you can just call Map and result will be the same.

TinyMapper介绍_第1张图片

More complex example

Let's take a look more complex sample. For example, we've following class.

public sealed class PersonComplex
{
    public Address Address { get; set; }
    public DateTime CreateTime { get; set; }
    public List<string> Emails { get; set; }
    public string FirstName { get; set; }
    public Guid Id { get; set; }
    public string LastName { get; set; }
    public string Nickname { get; set; }
}

public sealed class Address
{
    public string Phone { get; set; }
    public string Street { get; set; }
    public string ZipCode { get; set; }
}

and we don't want map CreateTime and Nickname properties.

TinyMapper介绍_第2张图片

As you can see, we've just marked CreateTime and Nickname as ignored.

Under the hood

It's a long story. In the next article, I'll describe in details how TinyMapper works. Internally TinyMappergenerates mapping code thru ILGenerator. The mapping code looks almost the same a handwritten code. For instance, following code was generated for Person to PersonDto mapping.

TinyMapper介绍_第3张图片

I hope you enjoyed it. Thanks for reading the article.


你可能感兴趣的:(TinyMapper)