原文: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 = "support@tinymapper.net",
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 Perso
n
class 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 = "support@tinymapper.net",
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 typeMap
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.
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.
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 TinyMapper
generates 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.
I hope you enjoyed it. Thanks for reading the article.