Introduction
How many times did you code trivial mapping like:
Address myaddress = new Address {
.Town = myPersonAddress.City,
.Street = myPersonAddress.Street
}
Town
maps to City
, and then in the next class Town
is called Locality
, in Dutch it maps to plaats or stad. Housenumber
maps to Housnr
or Nr
or Number
or whatever. Furthermore, why do we need to map properties with the same name (Street
)).
All this mapping becomes annoying and in the case of this address example, I recently was surprised how many times I had to translate it from one type in this namespace to that (proxy) type in that namespace.
So the PropertyMapper
was born. Given two objects, set the properties of the To
-object with the values of the From
object if their names are the same or if the property names are mapped to each other, but only when the property is not excluded from the mapping.
In other words, given the above example, we will map Town
and City
while Street
is mapped automatically unless we exclude it.
Automatic mapping comes with a price because, instead of a one by one direct coupling, two property collections have to be mapped to each other. So there is a speed issue.
We can map by reflection, but that is a little slow, we can map using Emit, but the Emit language is not my kind of sport. So, we will map using Expressions. The getter of the From
and the setter of the To
are translated to a Func
and an Action
delegate, which are wrapped in a separate class. Mapping means calling these delegates directly through this class.
A mapping between properties of type one and two won’t change, and an Expression
must be compiled. So we separate the creation of the mapping and the compiling of its expressions from the applying of the mapping. The method RegisterAMapping
creates the mapping, the method Map
applies the mapping.
Town
to City
is mapped by a property map. This property map is a dictionary
with string
keys and values. We make a dictionary entry City
(key) _town
(value) and one Town
(key) _town
(value). We map on value, in this case on _town
(but you can use your own). For the registration of the mapping, it is of no importance if the map really exists. So if you add a dictionary
entry City
(Locality
) _town
(value), the From
and To
Type don’t need to have a Locality
property. So you make big list of mapping for each ‘address entity’ which you can use over and over again.
Example:
Dictionary<string, string> addresspropertiesmap = new Dictionary<string, string>();
addresspropertiesmap.Add("City", "_town");
addresspropertiesmap.Add("Town", "_town");
addresspropertiesmap.Add("Locality", "_town");
addresspropertiesmap.Add("ZipCode", "_zip");
addresspropertiesmap.Add("Nr", "_housenr");
addresspropertiesmap.Add("HouseNr", "_housenr");
addresspropertiesmap.Add("PostalCode", "_zip");;
We registrate the mapping like:
PropertyMapper.Mapper.RegisterAMapping(typeof(Address), typeof(Address2), addresspropertiesmap, "Address", "Address2");
After this registration, you can write:
PropertyMapper.Mapper.Map(address, address3);
Where Values of address3
are set to the mapped properties of address
, no matter whether that address
has a Town
property and address3
has a City
one.
Is it Fast?
You can beat direct mapping in any way, but on my machine, mapping 100.000 times of address3
to address
took an average of 127 milliseconds. Of course, this is without the creation of the mapping, but you can do that at startup.
Conversion
In my previous version, I hadn’t included conversion. If two types differ, you have to convert the value of the fromtype
to the equivalent value of the totype
.
A word of caution: bear in mind that a Property
Mapper is not the way to go when dealing with advance casting scenarios. In these cases, leave automatic mapping for the properties involved for what it is and code the mapping yourself.
There are several standard ways to do conversion: by casting, by 'Convert
', by 'TryParse
' (in different variants) or by implementing an interface in a custom class like the IConvertible
interface, to just call a few. The important difference between TryParse
and Convert
is that Convert
raises an exception when conversion fails, while TryParse
returns a false
as a return value.
I added a ConversionMapperFactory
that specifies a standard conversion for combinations of FromType
to ToType
. Per combination, you can choose between NoConversion
, UseCast
, UseConvert
, UseTryParse
and UseIConversion
.
The latter one, the custom conversion, I must explain, the other ones speak for themselves. I decided not to use the IConvertible
interface, because an Interface
, by definition, means implementing all methods while you probably only want one or two. So I implemented another known technique.
The tricks is as follows:
public Interface IConvertable{}
public Interface IConvertable<T, R> : IConvertable{
T Convert(R input);
}
The non-generic interface is useless to implement because it holds no members, the generic one inherits from it. That means you can use reflection to search for the non generic one while finding the generic one. The IConvertable<T,R>
interface has only one method, but in one class you can implement this interface multiple times each time with different concrete typing like:
private class ConvertManager : IConversion<DateTime, string>, IConversion<DateTime, bool>
This class implements only two conversion implementations from string
to datetime
and bool
to datetime
(which is nonsense) instead of implementing all in case I had decided to use the IConvertible
interface.
We getting flexibility just by adding or removing specific typed IConvertable<T, R>
interfaces, while maintaining the rigid nature of interfaces where you need to implement every interface method; in our case one!
How does the propertymanager
know which interfaces are implemented? Of course, we can use Reflection, but needless to say, the invocation is done by Expressions. The core implementation is:
handlerType = handler.GetType();
MethodInfo minfo = handlerType.GetMethods().Where(m => m.Name == "Convert").Where(mm =>
{
return fromType == mm.GetParameters()[0].ParameterType && toType == mm.ReturnType;
}).First();
var fromparameter = Expression.Parameter(fromType, "from");
var toparameter = Expression.Parameter(toType, "to");
var resultparameter = Expression.Parameter(toType, "result");
var invocationExpression =
Expression.Lambda(
Expression.Block(
Expression.Call(
Expression.Convert(Expression.Constant(handler), handlerType),
minfo, new ParameterExpression[] { fromparameter }
)), fromparameter);
return invocationExpressionCompile();
The ConvertDispatcher
class has all the code.
For the rest, I should say explore the code.
What’s Next?
For my purpose, I didn’t need to make my Mapper
class thread safe. Static
methods should always take into account thread safety.