Automapper: List to members

2019-07-25 12:28发布

I'd like to map an arbitrary list of abstract types to an arbitrary set of properties, that share the same base type. Here is some UnitTest code, which currently fails and which I want to success. Could you help me, get a generic solution?

Here are the classes:

 public class Source
{
    public string Name { get; set; } = "SomeName";
    public Dictionary<string, ValueType> SourceList { get; set; } = new Dictionary<string, ValueType>();
}
public interface IDestination
{
    string Name { get; set; }
}

public class Destination : IDestination     //And many other classes like this, with other properties inherited from ValueType
{
    public string Name { get; set; }

    public double DoubleValue { get; set; }
    public int IntValue { get; set; }

    public string SomeOtherProperty { get; set; }
}

And here is the unit test, I'd like to succeed:

    [TestMethod]
    public void TestMethod1()
    {
        var source = new Source();
        source.SourceList.Add("IntValue", (int) 3);
        source.SourceList.Add("DoubleValue", (double) 3.14);

        Mapper.Initialize(config =>
        {
            //Put in some magic code here!!!
        });

        var destinationAbstract = Mapper.Map<Source, IDestination>(source);     //the type of destination is known only at runtime. Therefore Mapping to Interface

        var destination = (Destination) destinationAbstract;
        Assert.AreEqual(source.Name, destination.Name);
        Assert.AreEqual((int)source.SourceList["IntValue"], destination.IntValue);
        Assert.AreEqual((double)source.SourceList["DoubleValue"], destination.DoubleValue);
    }

Please be aware, that

  • the number of classes, that inherit from IDestination is only known at runtime
  • the content of the SourceList may be different for each Source-instance and therefore the properties of the destination class could also change for each class definition

I hope you can help me, because I wasn't able to determine a generic solution with the help of the documentation.

Thanks in advance.

标签: c# AutoMapper
2条回答
对你真心纯属浪费
2楼-- · 2019-07-25 12:51

You can map from Dictionary<string, object>(property names to property values) to some class by default, without any extra configuration. The docs and the tests.

查看更多
我命由我不由天
3楼-- · 2019-07-25 12:51

After considering Lucians hint and after trying different things with Automapper, I finally found a solution for my initial unit-test:

    [TestMethod]
    public void TestMethod1()
    {
        var source = new Source();
        source.SourceList.Add("IntValue", (int) 3);
        source.SourceList.Add("DoubleValue", (double) 3.14);

        Mapper.Initialize(config =>
        {
            //"Magic code"
            config.CreateMap<Source, IDestination>();
            config.CreateMap(typeof(Source), typeof(Destination)).IncludeBase(typeof(Source), typeof(IDestination));
        });

        //standard map-call
        var destination = Mapper.Map<Destination>(source);
        //Additional "Trick":
        Dictionary<string, object> mappingDict =
            source.SourceList.ToDictionary(pair => pair.Key, pair => (object) pair.Value);
        Mapper.Map(mappingDict, destination, typeof(Dictionary<string, object>), typeof(Destination));

        Assert.AreEqual(source.Name, destination.Name);
        Assert.AreEqual(source.SourceList["IntValue"], destination.IntValue);
        Assert.AreEqual(source.SourceList["DoubleValue"], destination.DoubleValue);
    }

The "trick" is to cast my Dictionary<string, ValueType> to Dictionary<string,object> and to map this dictionary-member to the destination object in addition(!) to the standard map-call. This works, but has some drawbacks:

  1. Mapping validation is not possible (Validation says: either source member "SourceList" is not mapped or the destination members "DoubleValue" or "IntValue" are not mapped)
  2. Casting the dictionary is kind of ugly (and seems unnecessary to me...)
  3. I need 2 calls to Mapper.Map instead of only one.

It seems to me, that there is no other way to solve my initial problem. But I am open for any suggestions or improvements to my solution. The initial problem could be solved also easily through using reflections, so all information for a proper mapping setup should be existent, but I was not able to find this proper mapping setup.

查看更多
登录 后发表回答