3

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.

Mau
  • 14,234
  • 2
  • 31
  • 52

1 Answers1

8

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.

Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
  • I see, thanks! Regarding the second part of the answer, that wouldn't be an option as the method this code is in gets the KVP as an `object` reference, i.e. the type parameters are not known. – Mau Sep 09 '14 at 14:39
  • Ah, I see, that's unfortunate – Andrew Whitaker Sep 09 '14 at 14:40
  • @Mau: Another thing you could do is first map to `DictionaryEntry`, and then map to `KeyValuePair`. In that case you could use the `ConstructUsing` method – Andrew Whitaker Sep 09 '14 at 14:57
  • When I have a `DictionaryEntry`, then I can just statically build a `KVP` :-) – Mau Sep 09 '14 at 15:04