0

I have an object which contains two different object types, I am trying to map it to another object which will contain a single list.

In this simple minimal reproducible example I have created a zoo class which contains a list of animals this is the destination. The source is the MammelHouse class which contains lists of pig and cow objects.

My issue is when I try to add the pigs to the list then it over writes the already written cows. I need both objects to be added to this list.

My current solution is to map each object type alone and then add them to the main object, as my actual application has ten different types currently this is not an optimal solution. I am hoping that there is a way to solve this with automapper directly.

current mapping attempt

public class MappingResourceZoo : Profile
    {
        public MappingResourceZoo()
        {
            CreateMap<MammalHouse, Zoo>()
                .ForMember(dest => dest.Animals, opt => opt.MapFrom(src => src.Cows))
                .ForMember(dest => dest.Animals, opt => opt.MapFrom(src => src.Pigs));

            CreateMap<Cow, Animal>()
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Species));
            
            CreateMap<Pig, Animal>()
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Species));
        }
    }

The issue with this mapping is that when pigs are mapped the cows are over written. So the test fails.,

Test

    [Fact]
    public void TestsConcatAddress()
    {
        var src = new MammalHouse()
        {
            Cows = new List<Cow>()
            {
                new Cow() {Species= "Chocolate cow"},
                new Cow() {Species= "Vanilla cow"}
            },
            Pigs = new List<Pig>()
            {
                new Pig() { Species= "Bacon"},
                new Pig() { Species= "Sausage"}
            }
        };

        var config = new MapperConfiguration(cfg => cfg.AddProfile<MappingResourceZoo>());
        var mapper = config.CreateMapper();
        var response = mapper.Map<Zoo>(src);

        response.Animals.Should().NotBeNull().And.HaveCount(4); ;
    }

Here is the model for your testing pleasure. These two models can not be altered as they come from a third party system.

// The Zoo model comes from third party api 1


public class Zoo
{
    public List<Animal> Animals { get; set; }
    
}
public class Animal 
{
    public string Name { get; set; }
}

// The mammal model comes from third party api Two.

public class MammalHouse
{
    public List<Cow> Cows { get; set; }
    public List<Pig> Pigs { get; set; }
    
}

public class Cow
{
    public string Species{ get; set; } = "cow";
}    

public class Pig
{
    public string Species{ get; set; } = "pig";
}

What i have looked at

  • Add AfterMap This doesn't seam to be the right direction as i actually have more then just two.
  • Currently trying to get it to work using a custom mapper but that doesn't seam to work either as i cant pass it the mapper object.

update from comments

From the comment suggested apparently Concat isnt allowed.

CreateMap<MammalHouse, Zoo>()
            .ForMember(dest => dest.Animals, opt => opt.MapFrom(src => src.Cows.Concat(src.Pigs)));

enter image description here

Tried add range as well this doent work either becouse cows and pigs are not the same type

opt.MapFrom(src => src.Cows.AddRange(src.Pigs)));
Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • You could use the extension Nuget package prepared by AutoMapper: https://github.com/AutoMapper/AutoMapper.Collection It should handle such cases – Pawel Maga Jun 04 '21 at 08:23
  • 1
    `.ForMember(dest => dest.Animals, opt => opt.MapFrom(src => src.Cows.Concat(src.Pigs)))` Also the `MapFrom`s for `Name` are useless. The second `MapFrom` for `Animals` simply overwrites the first one :) Check [the execution plan](https://docs.automapper.org/en/latest/Understanding-your-mapping.html). – Lucian Bargaoanu Jun 04 '21 at 08:24
  • Error trying to add concat doesn't seam that's allowed. I didnt know you could see the execution plan this is going to be quite useful thank you – Linda Lawton - DaImTo Jun 04 '21 at 08:35
  • You need to try a little harder :) That's a very basic compile error. – Lucian Bargaoanu Jun 04 '21 at 08:40
  • Well it was your code kind of assumed you knew it was supported or not ‍♀️ Not sure how cow and pig objects could be contacted really unless it would happen after the mapping takes place. – Linda Lawton - DaImTo Jun 04 '21 at 08:43
  • Would the -1 care to comment I would be happy to clear up any confusion. I have been working on this for serval hours that's why i created the dedicated model for a SO question to make it a clean [example] – Linda Lawton - DaImTo Jun 04 '21 at 08:52
  • @LucianBargaoanu could you clarify what you mean by them being useless? Cow and pig are two different objects. Why would they over write each other? As you can see i am still learning automapper – Linda Lawton - DaImTo Jun 04 '21 at 09:04

1 Answers1

1

Add a common interface IMammel to Cowand Pig and then use Concat

Model

public interface IMammel 
{

}
public class Animal 
{
    public string Name { get; set; }

}
public class Pig : IMammel 
{
    public string Species { get; set; } = "pig";
}


public class Cow : IMammel 
{
    public string Species { get; set; } = "cow";
}

AutoMapper Configuraition

public class MappingResourceZoo : Profile
{
    public MappingResourceZoo()
    {
        CreateMap<Cow, Animal>()
         .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Species));

        CreateMap<Pig, Animal>()
            .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Species));

        CreateMap<MammalHouse, Zoo>()
            .ForMember(dest => dest.Animals, opt => opt.MapFrom(src => src.Cows.Concat<IMammel>(src.Pigs)));

    } 
}
Akshay G
  • 2,070
  • 1
  • 15
  • 33
  • 1
    Unfortunately in the real world zoo and mammal house are two object models coming from different systems. I cant alter them to create a cross link – Linda Lawton - DaImTo Jun 04 '21 at 11:03
  • The concat isn't allowed ine the question diverted me to see why it wasnt allowed – Akshay G Jun 04 '21 at 11:06
  • see if this helps https://stackoverflow.com/questions/47808827/mapping-unrelated-collections-into-one – Akshay G Jun 04 '21 at 11:08
  • 1
    I actually got it working thanks. I was able to create an IMammel which then let me do what you mentioned. Im going to accept this, as it technically answers the question I asked.. Now to see if I can implement it in the real world project, without altering the ordinal models. Thank you for your help. – Linda Lawton - DaImTo Jun 04 '21 at 11:12
  • I had just added `IAnimal` to `Pig` and `Cow` and not to `Animal`, so there was no cross-link. Maybe `IMammel` would have given the clear picture .. Updated the Answer with the naming... I thought you had no control to alter both of the systems. – Akshay G Jun 04 '21 at 11:32
  • Yeah I dont, which is why its not really working trying to add it to the real world system. I was hoping i could use the interface that it has but i cant. – Linda Lawton - DaImTo Jun 04 '21 at 12:10
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/233323/discussion-between-akshay-gaonkar-and-daimto). – Akshay G Jun 04 '21 at 12:13