Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using AutoMapper to merge objects

I'm attempting to use AutoMapper to merge data from multiple objects, and I'm running into a couple issues that I can't seem to solve.

I have an object like:

public class Parent
{
    public string Id { get; set; }
    public List<Child> Children { get; set; }
}
public class Child
{
    public string Key { get; set; }
    public int? Value1 { get; set; }
    public int? Value2 { get; set; }
    public int? Value3 { get; set; }
    public int? Value4 { get; set; }
    public int? Value5 { get; set; }
}

Obviously the child properties are not all ints, but most of them are nullable.

I have these objects in two different tiers of my application, so I'm using AutoMapper to convert between them:

{
    Mapper.CreateMap<Parent, ParentDTO>();
    Mapper.CreateMap<ParentDTO, Parent>();

    Mapper.CreateMap<Child, ChildDTO>();
    Mapper.CreateMap<ChildDTO, Child>();
}

The conversion works well, and I'm happy with what it does. However, now I have a need to merge multiple objects of the same type. I will have one copy of an object which has most or all of the properties populated, and another copy which only has a few, with the rest being null. I want any non-null properties of the second (partial) object to be mapped to the respective field in the first (already filled out) object. As the accepted answer to this answer states, I should be able to use AutoMapper to do this as well, but it doesn't give any clear examples.

However, when I go to perform the operation, I get an object that is identical to either of the objects, not combined like I want.

{
    var bigParent = new Parent
    {
        Id = "14",
        Children = new List<Child>
        {
            new Child
            {
                Key = "A",
                Value1 = 10,
                Value2 = 20,
                Value3 = 30,
                Value4 = 40,
                Value5 = 50
            }                   
        }
    };

    var merge = new Parent
    {
        Id = "14",
        Children = new List<Child>
        {
            new Child
            {
                Key = "A",
                Value1 = null,
                Value2 = null,
                Value3 = 100,
                Value4 = null,
                Value5 = null
            }
        }
    };

    var res = Mapper.Map(merge, bigParent);
}

I am expecting Child to have values of 10, 20, 100, 40 and 50. However, depending on if I put merge as source or destination in Mapper.Map I get either null, null, 100, null, null or 10, 20, 30, 40, 50.

Is there a way I can get my expected values? I'm thinking having the List is what is causing the issues, since it won't know how to line up the entities (to determine if they are the same or not). If is answers any questions, I would be able to identify if child records are the same by seeing if one or more properties are the same (in this example, Key).

like image 732
kingofzeal Avatar asked Oct 08 '15 14:10

kingofzeal


2 Answers

If you want to ignore null values in mappings defined in AutoMapper Profile, use:

public class MappingProfile : AutoMapper.Profile
{
  public MappingProfile()
  {
     this.CreateMap<Parent, Parent>()
         .ForAllMembers(o => o.Condition((source, destination, member) => member != null));
  }
}
like image 145
Marta Avatar answered Oct 27 '22 00:10

Marta


Automapper is capable of this, your mapper needs to be configured as such:

Mapper.Initialize(cfg =>
{
    // necessary if you are mapping parent to a parent
    cfg.CreateMap<Parent, Parent>()
        .ForAllMembers(options =>
        {
            options.Condition(src => src.DestinationValue == null);
        });
    // necessary if you are mapping your child to a child
    cfg.CreateMap<Child, Child>()
        .ForAllMembers(options =>
        {
            options.Condition(src => src.DestinationValue == null);
        });
});

And then your usage as such:

var bigParent = new Parent
{
    Id = "14",
    Children = new List<Child>
    {
        new Child
        {
            Key = "A",
            Value1 = 10,
            Value2 = 20,
            Value3 = 30,
            Value4 = 40,
            Value5 = 50
        }
    }
};

var merge = new Parent
{
    Id = "14",
    Children = new List<Child>
    {
        new Child
        {
            Key = "A",
            Value1 = null,
            Value2 = null,
            Value3 = 100,
            Value4 = null,
            Value5 = null
        }
    }
};

var bigChild = new Child
{
    Key = "A",
    Value1 = 10,
    Value2 = 20,
    Value3 = 30,
    Value4 = 40,
    Value5 = 50
};

var mergeChild = new Child
{
    Key = "A",
    Value1 = null,
    Value2 = null,
    Value3 = 100,
    Value4 = null,
    Value5 = null
};

Mapper.Map(bigChild, mergeChild);
Debug.Assert(mergeChild.Value3 == 100);

Mapper.Map(bigParent, merge);
Debug.Assert(merge.Children[0].Value3 == 100);
like image 31
Ruskin Avatar answered Oct 26 '22 23:10

Ruskin