Introduction
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 object mappers are, so let's talk about performance briefly.
We'll use the same Person
class for the 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 the Person
. We'll compare:
During the 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 through 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 the target type
Map
source object to the target type
Quote:
Note: if you're multithreading an application, register all bindings
on application start. While the application is running, you'll call only Map
method. So, you'll get speed and thread safe.
TinyMapper
can do the first step for you, i.e., you can just call Map
and the result will be the same.
Quote:
Note: Be careful to call Map
without Bind
, it's not thread safe.
More Complex Example
Let's take a look at a more complex sample. For example, we have the following classes.
public sealed class PersonComplex
{
public Address Address { get; set; }
public DateTime CreateTime { get; set; }
public IReadOnlyList<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 to map CreateTime
and Nickname
properties.
As you can see, we've just marked CreateTime
and Nickname
as ignored. Bind LastName
from the source Surname
to the target class and bind IList
of emails to List
TinyMapper
allows:
Ignore
fields
Map
one field to a different field
Map
interface to exact type
Custom Mapping
For instance, we have the following classes:
public sealed class PersonCustomSource
{
public IList<string> Emails { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public sealed class PersonCustomTarget
{
public IList<string> Emails { get; set; }
public string FullName { get; set; }
}
We want to map:
FirsName
and LastName
to FullName
with the following convention "FirstName LastName
"
TinyMapper
supports TypeConverter, i.e., you can create your own TypeConverter
for any type.
public sealed class PersonTypeConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(PersonCustomTarget);
}
public override object ConvertTo
(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
var concreteValue = (PersonCustomSource)value;
var result = new PersonCustomTarget
{
FullName = string.Format("{0} {1}", concreteValue.FirstName, concreteValue.LastName),
Emails = new List<string>(concreteValue.Emails)
};
return result;
}
}
The next step is to register the type
converter. TypeConverter
supports registration through attribute...
[TypeConverter(typeof(PersonTypeConverter))]
public sealed class PersonCustomSource
{
public IList<string> Emails { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
...or code
TypeDescriptor.AddAttributes(typeof(PersonCustomSource),
new TypeConverterAttribute(typeof(PersonTypeConverter)));
Here's an additional sample of how to implement a type converter.
Under the Hood
It's a long story. In the next article, I'll describe in detail about how TinyMapper
works. Internally, TinyMapper
generates mapping code through ILGenerator. The mapping code looks almost the same as handwritten code. For instance, the following code was generated for Person
to PersonDto
mapping.
I hope you enjoyed it. Thanks for reading the article.