Introduction
This article describes briefly the features of H-Mapper, which is a .NET library available in Nuget written by me.
More information can be found at https://minhlhuong.wordpress.com/hmapper/.
Background
Mapping from one object to another is a task that many developers have to do in back-end developments. Many mappers already automate this task for them, so why use another one again? This is one of the purposes of this document.
Benefits
So here is what H-Mapper may do better than others:
- Performance
- Specify inclusions
- Strongly type mapping declaration of generic types.
Performance
Some performance tests have been done in various configurations, and the results are displayed here:
Specify Inclusions
When dealing with large objects, and especially when they contain collections, it is often a bad idea to map the whole thing, so developers tend to create a lighter version of the target class. Instead of doing that, H-Mapper offers another possibility. Use the same target type, but specify the objects to include at execution time.
Strongly Type Mapping Declaration of Generic Types
When declaring a mapping for generic classes, other mappers have no other way than propose the use of the "typeof
" method. Alas, in this case, it is not possible to benefit from the intellisense of Visual Studio. Also, it is hard to map a property to a custom delegate in such cases. H-Mapper offers a way to map generic types in a strongly typed manner.
Using the Code
First of all, let's declare a simple mapping. This is done in a static
method of the MapConfig
class.
MapConfig.Initialize(initializer =>
{
initializer.Map<Class1, DTO.Class1>();
}
In this document, I omit the namespace when I refer to the source type. The target type is prefixed by "DTO
".
We can now map an instance of the source type in this way:
var source = new Class1();
var MyDTO = Mapper.Map<Class1, DTO.Class1>(source);
We can also fill an existing instance of the target type:
Mapper.Fill<Class1, DTO.Class1>(source, target);
We can specify "object
" as the type of the source object, and then create an extension method that omits the type of the source instance, but keep in mind that it comes with a loss of performance.
public static TTarget Map<TTarget>(this object source)
{
return Mapper.Map<object, TTarget>(source);
}
We can decide to map a property to another one when the names don't match.
initializer.Map<Class1, DTO.Class1>()
.WithMember(x => x.Key, api => api.LinkTo(x => x.Id))
Or, even a custom delegate or lambda expression.
.WithMember(x => x.Date_Plus_2, api => api.LinkTo(x => x.Date.AddDays(2)))
Specify Inclusions
Now, here is how we specify inclusions:
var myDTO = Mapper.Map<Class1, DTO.Class1>(source,
x => x.Coll.Select(y => y.CollProp), x => x.Prop);
In this example, we tells the mapper that we want to include Coll
property, which is a collection type, and include CollProp
property of the collection as well. We can specify the inclusions one after the other. In our example, Prop
property is also retrieved. All other properties will be ignored.
Declare Mapping for Generics
Here is how we declare mapping for generics.
initializer.Map<Business.SimpleGeneric2<TGen1>, DTO.SimpleGeneric2<TGen1>>()
.WithMember(x=>x.AnotherGenericProperty, api => api.LinkTo(x=>x.GenericProperty))
H-Mapper offers special Argument types TGen1
,..TGen9
we can use to map generic types. They all derive from IGeneric
. We can also create our own IGeneric
should we need to add constraints on the generic argument.
We can even map a property to a lambda expression, which is quite impossible with the other mappers. The delegate could be as fancy as :
.WithMember(x=>x.Prop,
api=>api.LinkTo(arr=>arr.Select(item => new Class2() { Prop2=5}).ToArray()));
The returned type is an array of Class2
, which will then be mapped to a collection of DTO.Class2
(depending on the type of the collection in the target type).
Mapping An Unknown Target Type
If we don't know in advance what the type of the target is, we can specify Object
as the target type.
object source; object target = Mapper.Map<object, object>(source);
The target type will then be taken from the first target type matching the type of the source object. If more than one type matches the source type, expect to have the result to be "random"..
This is specially useful when the target type is an ArrayList
(which is a collection of objects).
Mapping a Whole Class to a Lambda Expression
This can be done with the ManualMap
method:
initializer.ManualMap((ManuallyMappedClass x) => Tuple.Create(x.Id, x.Title));
In this example, we map a class to a Tuple
.
This is specially useful when we want to use a converter for enum
s:
initializer.ManualMap((MyEnum val) => DTO.Convert(val));
Other Features
H-Mapper also supports many other things like polymorphism (with no extra configuration), enum
, struct
, circular references.
History