AutoMapper: Map Interface to Abstract class - is t

2019-04-30 04:36发布

问题:

I'm using AutoMapper to map objects between different layers of my application. On the one side I have an interface which looks like this:

public interface MyRepo 
{
    IEnumerable<IRootObject> GetItems(param);
}

IRootObject looks like this:

public interface IRootObject
{
    int Id { get; set; }
    DateTime? Date { get; set; }
}

public interface IFirstSubObject : IRootObject 
{
    string MyFirstProp { get; set; }
}

public interface ISecondSubObject : IRootObject 
{
    string MySecondProp { get; set; }
}

So the GetItems()call actually returns an array of only IxxxSubObjectItems. On the other side the structure looks like this:

public abstract class MyCustomRoot
{
    protected MyCustomRoot(){}
    public int Id { get; set; }
    public DateTime? Date { get; set; }
}

public class MyCustomFirstSub : MyCustomRoot
{
    public MyCustomFirstSub() : base() {} 
    public string MyFirstProp { get; set; }
}

public class MyCustomSecondSub : MyCustomRoot
{
    public MyCustomSecondSub () : base() {} 
    public string MySecondProp { get; set; }
}

Now I've set up the mapper like so

AutoMapper.Mapper.CreateMap<IRootObject, MyCustomRoot>
    .Include<IFirstSubObject, MyCustomFirstSub>
    .Include<ISecondSubObject, MyCustomSecondSub>();

AutoMapper.Mapper.CreateMap<IFirstSubObject, MyCustomFirstSub>();
AutoMapper.Mapper.CreateMap<ISecondSubObject, MyCustomSecondSub>();

But I keep getting MapperExceptions ("Cannot construct abstract class", which makes sense in a way). I also call AssertConfigurationIsValid, and that passes for this code.
If I don't make the MyCustomRoot class abstract, then the mapping works, but I get a list of MyCustomRoot objects, while I actually would like to have a list of MyCustomxxxSub, because there is a Factory later on which uses this type to generate the correct UI...

I hope someone can point me in the right direction for this one!
Thanks!

回答1:

Ok I found a way to actually do this! So with the Interfaces and Classes provided above, you can do something like this
(Remember: the MyCustomRoot class is abstract)

AutoMapper.Mapper.CreateMap<IRootObject, MyCustomRoot>
    .Include<IFirstSubObject, MyCustomFirstSub>
    .Include<ISecondSubObject, MyCustomSecondSub>();

and then use the followig Map call to map the objects:

public List<MyCustomRoot> PerformMapping(List<IRootObject> rootObjects)
{
    var returnList = new List<CustomRoot>();
    foreach(var rootObject in rootObjects)
    {
        var abstractObject = (MyCustomRoot)Mapper.Map(rootObject, rootObject.GetType(), typeof(MyCustomRoot));
        returnList.Add(abstractObject);
    }
    return returnList;
}

Now I actually get an array of MyCustomRoot objects, which are all the specific implementations.

This is exactly what I was looking for!



回答2:

If so, then you want to map to that factory, inject it and let it create the correct instance in due time, since it's the factory that apparently knows what to instantiate.