Search code examples
.netgenericsautomapperautomapper-3

No mapping with AutoMapper with KeyValuePair<K, V>


I'm building a generic pretty-print method. One of the special types that I want to handle separately is KeyValuePair<TK,TV>. In order to reduce the object to a known type, I thought I would map each KeyValuePair<TK,TV> to a KeyValuePair<object, object>.

The following code always produces 2 nulls in the Key, Value properties of proxy.

Mapper.CreateMap(o.GetType(), typeof(KeyValuePair<object, object>));
var proxy = Mapper.Map<KeyValuePair<object, object>>(o);

This non-generic version, on the other hand, works as expected:

Mapper.CreateMap(o.GetType(), typeof(DictionaryEntry));
var proxy = Mapper.Map<DictionaryEntry>(o);

Why?

o at this stage has been tested to be a KeyValuePair<,>.
I'm using AutoMapper 3.2.1.0 on .NET 4.0.


Solution

  • DictionaryEntry's Key and Value are both settable. When you map to DictionaryEntry, AutoMapper matches the Key and Value property and sets them.

    It's unable to do this with KeyValuePair<TKey, TValue> since it is immutable. Therefore AutoMapper returns a new KeyValuePair<object, object> with the Key and Value properties unset.

    Normally, you could use ConstructUsing to get around this:

    Mapper.CreateMap<KeyValuePair<string, string>, KeyValuePair<object, object>>()
        .ConstructUsing(kvp => new KeyValuePair<object, object>(kvp.Key, kvp.Value));
    

    However, since you're not using this version of CreateMap, this isn't possible.

    You could create a simple extension method to do this instead though:

    public static class KeyValuePairExtensions
    {
        public static KeyValuePair<object, object> CastUp<TKey, TValue>(
            this KeyValuePair<TKey, TValue> kvp)
        {
            return new KeyValuePair<object, object>(kvp.Key, kvp.Value);
        }
    }
    

    And then use it instead of AutoMapper:

    var kvp = new KeyValuePair<string, string>("Hello", "World");
    KeyValuePair<object, object> proxy = kvp.CastUp();
    

    This would prevent you having to create a different mapping definition for every KeyValuePair variation you use.