Linda Lawton - DaImTo
Linda Lawton - DaImTo

Reputation: 117196

How to map lists without overwriting values

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

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)));

Upvotes: 0

Views: 446

Answers (1)

Akshay G
Akshay G

Reputation: 2280

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)));

    } 
}

Upvotes: 1

Related Questions