致力于支持遗留代码,基本上我需要的是将两个对象合并在一起。一个是来自UI,一切都是null或空集合(我不想要那个),第二个是相同的,从数据库新鲜。我想覆盖从源到目的地的非空,非空值,保持其他一切完整。我这样做:
Mapper.Map(model, result);
我使用以下方法解决不需要的属性:
cfg.CreateMap<ClientItem, ClientItem>().ForAllMembers(a =>
{
a.ResolveUsing<IgnoreNullSourceValues, object>(a.DestinationMember.Name);
});
我的自定义类是:
class IgnoreNullSourceValues : IMemberValueResolver<object, object, object, object>
{
public object Resolve(object source, object destination, object sourceMember, object destinationMember, ResolutionContext context)
{
if (sourceMember is IList)
{
var a = (IList)sourceMember;
if (a.Count == 0)
return destinationMember;
}
if (sourceMember is DateTime)
{
var a = (DateTime)sourceMember;
if (a == DateTime.MinValue)
return destinationMember;
}
return sourceMember ?? destinationMember;
}
}
它适用于所有内容,但是当我调试时,我看到例如sourceMember上的空List,destinationMember中的好列表,我看到它返回destinationMember,但是在setter中我看到它然后绑定空列表。我怎么能克服这个?
答案 0 :(得分:0)
AutoMapper似乎在调用Clear()
之后但在设置新值之前调用目标列表上的Resolve()
。这意味着destinationMember
的值实际上会在调用setter之前发生变化。您可以(粗略地)通过将IList
的类型更改为ObservableCollection
并监听目标集合上的更改来确认这一点。例如:
var clearedCollection = new ObservableCollection<int>() { 1, 2, 3 };
clearedCollection.CollectionChanged += ClearedCollection_CollectionChanged;
var model = new ClientItem() { Test = new ObservableCollection<int>() };
var result = new ClientItem() { Test = clearedCollection };
Mapper.Map(model, result);
class ClientItem
{
private ObservableCollection<int> test;
public ObservableCollection<int> Test
{
get
{
return test;
}
set
{
Console.Out.WriteLine("Set");
test = value;
}
}
}
private static void ClearedCollection_CollectionChanged(object sender,
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Console.Out.WriteLine(e.Action);
}
public object Resolve(object source, object destination, object sourceMember, object destinationMember, ResolutionContext context)
{
Console.Out.WriteLine("Resolve");
...
}
将输出
Set
Set
Resolve
Reset
Add
Add
Add
Set
前两个set
用于初始化ClientItems。调用Resolve
后,目标为Reset
,以便从Resolve
返回的列表中的项目可以Add
。由于从Resolve
返回的列表与Reset
相同,因此无需添加任何内容。最后set
是将此空白列表添加回ClientItem的位置(这是您在调试时看到的内容)。
为了确保在操作过程中集合保持完整,您可以复制返回的值:
class IgnoreNullSourceValues : IMemberValueResolver<object, object, object,
object>
{
public object Resolve(object source, object destination, object sourceMember, object destinationMember, ResolutionContext context)
{
if (destinationMember is IList)
{
var a = (IList)sourceMember;
if (a == null || a.Count == 0)
return CopyIList(destinationMember as IList);
}
if (sourceMember is DateTime)
{
var a = (DateTime)sourceMember;
if (a == DateTime.MinValue)
return destinationMember;
}
return sourceMember ?? destinationMember;
}
private static IList CopyIList(IList list)
{
var ret = (IList)Activator.CreateInstance(list.GetType());
foreach (var item in list)
ret.Add(item);
return ret;
}
}
请注意,在上面的代码中,我将第一个if语句切换为destinationMember
而不是sourceMember
。这是因为如果sourceMember
为空,if语句将评估为false,即使它是IList
。在你的代码中这很好,但是在这种情况下需要调用copy方法。
此解决方法似乎表明这可能不是预期的行为。我可能会在AutoMapper问题跟踪器中提出问题,看看会发生什么变化。