Automapper does not map properly null List member, when the condition != null is specified

 ̄綄美尐妖づ 提交于 2020-07-18 05:11:19

问题


There is a problem when I try to map a null list (member) of an object, considering that I specified:

.ForAllMembers(opts => opts.Condition((src, dest, srcMember) =>
    srcMember != null
));
cfg.AllowNullCollections = true; // didn't help also

short example from code:

gi.PersonList = new List<Person>();
gi.PersonList.Add(new Person { Num = 1, Name = "John", Surname = "Scott" });
GeneralInfo gi2 = new GeneralInfo();
gi2.Qty = 3;

Mapper.Map<GeneralInfo, GeneralInfo>(gi2, gi);

gi.PersonList.Count = 0, how to fix that?

using System;
using System.Collections.Generic;
using AutoMapper;

public class Program
{
   public static void Main(string[] args)
    {
        Mapper.Initialize(cfg =>
        {
            cfg.AllowNullCollections = true;
            cfg.CreateMap<GeneralInfo, GeneralInfo>()
            .ForAllMembers(opts => opts.Condition((src, dest, srcMember) =>
                srcMember != null
            ));

        });
        GeneralInfo gi = new GeneralInfo();
        gi.Descr = "Test";
        gi.Dt = DateTime.Now;
        gi.Qty = 1;
        gi.PersonList = new List<Person>();
        gi.PersonList.Add(new Person { Num = 1, Name = "John", Surname = "Scott" });

        GeneralInfo gi2 = new GeneralInfo();
        gi2.Qty = 3;

        Console.WriteLine("Count antes de mapeo = " + gi.PersonList.Count);

        Mapper.Map<GeneralInfo, GeneralInfo>(gi2, gi);

        Console.WriteLine("Count despues de mapeo = " + gi.PersonList.Count);
        // Error : gi.PersonList.Count == 0 !!!! 
        //por que? si arriba esta: Condition((src, dest, srcMember) => srcMember != null ...

    }
}

class Person
{
    public int Num { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
}

class GeneralInfo
{
    public int? Qty { get; set; }
    public DateTime? Dt { get; set; }
    public string Descr { get; set; }
    public List<Person> PersonList { get; set; }
}

https://dotnetfiddle.net/N8fyJh


回答1:


This should work but I'm not sure if you want to micro manage it like that:

cfg.AllowNullCollections = true;
cfg.CreateMap<GeneralInfo, GeneralInfo>()
    .ForMember(x => x.PersonList, opts => opts.PreCondition((src) => src.PersonList != null));

Problem is the collections that are handled specifically (that's true for most mappers though AutoMapper is a bit weird in this case, it's not my favorite) and seem to require the destination collection to be initialized. As I can see, collections are not copied in entirety which makes sense, but you need to initialize and copy individual items (this is my deduction but does sound right).

I.e. even if you skip the source, destination would still end up reinitialized (empty).

Problem as it seems is the Condition which is, given their documentation, applied at some later point, at which time the destination has already been initialized.

PreCondition on the other hand has a different signature to be used like you intended, as it doesn't take actual values, just source is available.

The only solution that seems to work is to use "per member" PreCondition (like the above).


EDIT:
...or this (using the ForAllMembers), but a bit ugly, reflection etc.

cfg.CreateMap<GeneralInfo, GeneralInfo>()
.ForAllMembers(opts =>
    {
        opts.PreCondition((src, context) =>
        {
            // we can do this as you have a mapping in between the same types and no special handling
            // (i.e. destination member is the same as the source property)
            var property = opts.DestinationMember as System.Reflection.PropertyInfo;
            if (property == null) throw new InvalidOperationException();
            var value = property.GetValue(src);
            return value != null;
        });
    }
);

...but there doesn't seem to be any cleaner support for this.


EDIT (BUG & FINAL THOUGHTS):

Conditional mapping to existing collection doesn't work from version 5.2.0 #1918

As pointed out in the comment (by @LucianBargaoanu), this seems to be a bug really, as it's inconsistent in this 'corner' case (though I wouldn't agree on that, it's a pretty typical scenario) when mapping collections and passing the destination. And it pretty much renders the Condition useless in this case as the destination is already initialized/cleared.

The only solution indeed seems to be the PreCondition (but it has issues given the different signature, I'm personally not sure why they don't pass the same plethora of parameters into the PreCondition as well?).

And some more info here:

the relevant code (I think)

nest collection is clear when using Condition but not Ignore #1940

Collection property on destination object is overwritten despite Condition() returning false #2111

Null source collection emptying destination collection #2031




回答2:


Try like this;

gi = Mapper.Map<GeneralInfo, GeneralInfo>(gi2);

I encounterted with this problem recently and somehow if the destination and sources are same type, the destination parameter doesn't work as expected.

Also, I want to notify that it isn't relevant with just Collection objects. If you debug the gi object you will see that the other properties are remaining with old values too. Somehow Automapper doesn't change the property values which assigned before if you pass the destination instance as destionation parameter. I think, it is mostly relevant that Automapper was not designed for creating copy/clone objects.



来源:https://stackoverflow.com/questions/47834970/automapper-does-not-map-properly-null-list-member-when-the-condition-null-is

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!