AutoMapper:在将子集合1映射到子集合2时丢失未映射的属性值
本文关键字:映射 子集合 属性 AutoMapper 2时 | 更新日期: 2023-09-27 17:51:01
使用AutoMapper:当映射嵌套集合时,我希望任何未映射的属性保留其原始值。相反,它们被设置为null。
例子:
我有这四个类
(注意Test2Child
具有Name
属性,而Test1Child
没有):
public class Test1
{
public List<Test1Child> Children { get; set; }
}
public class Test2
{
public List<Test2Child> Children { get; set; }
}
public class Test1Child
{
public int Value { get; set; }
}
public class Test2Child
{
public string Name { get; set; }
public int Value { get; set; }
}
…和一个简单的映射设置。
Mapper.CreateMap<Test1, Test2>();
Mapper.CreateMap<Test1Child, Test2Child>().ForMember(m => m.Name, o => o.Ignore());
Mapper.AssertConfigurationIsValid(); // Ok
我希望在映射....期间保留Test2Child.Name
的原始值我希望这是任何未映射属性的默认行为。
当我直接从Test1Child
映射到Test2Child
,它工作得很好;映射Value
,保留Name
:
var a = new Test1Child {Value = 123};
var b = new Test2Child {Name = "fred", Value = 456};
Mapper.Map(a, b);
Assert.AreEqual(b.Value, 123); // Ok
Assert.AreEqual(b.Name, "fred"); // Ok
当映射为嵌套集合(List<Test1Child>
到List<Test2Child>
)时,Value
映射正确…但是Name
的原始值丢失了!
var c = new Test1 { Children = new List<Test1Child> { new Test1Child { Value = 123 } } };
var d = new Test2 { Children = new List<Test2Child> { new Test2Child { Name = "fred", Value = 456 } } };
Mapper.Map(c, d);
Assert.AreEqual(d.Children[0].Value, 123); // Ok
Assert.AreEqual(d.Children[0].Name, "fred"); // FAILS! Name is null.
如何解决这个问题?
正如@MightyMuke的回答评论中提到的那样,@PatrickSteele在这里提出了一个很好的观点:也许将每个项目从源列表自动映射到目标列表....没有意义即。"但如果一个列表有3个,另一个列表有5个呢?"
在我的例子中,我知道源列表和dest列表总是具有相同的长度,并且(重要的是)源列表中的第n项总是与dest列表中的第n项直接对应。
所以,这是有效的,但我对自己感觉不好....
Mapper.CreateMap<Test1, Test2>()
.ForMember(m => m.Children, o => o.Ignore())
.AfterMap((src, dest) =>
{
for (var i = 0; i < dest.Children.Count; i++)
Mapper.Map(src.Children[i], dest.Children[i]);
});
尝试使用UseDestinationValue
在这个类似的问题的答案中描述:Automapper用子对象覆盖列表中缺失的源属性
我遇到了同样的问题。基本上,automapper不知道列表对象中的键是什么以便将它们与原始的匹配,所以它会新建一个对象。如果希望属性保持不变,则需要帮助它理解如何匹配回原始项,以便可以映射更改。为了做到这一点,你需要有一个钥匙,而你现在没有。
尝试如下内容:
public class Test1
{
public List<Test1Child> Children { get; set; }
}
public class Test2
{
public List<Test2Child> Children { get; set; }
}
public class Test1Child
{
public int ChildId { get; set; }
public int Value { get; set; }
}
public class Test2Child
{
public int ChildId { get; set; }
public string Name { get; set; }
public int Value { get; set; }
public Test2Child()
{ }
public Test2Child(int childId)
{
// of course you will need to load this from your data source, but for testing. :)
if (childId == 1)
{
ChildId = 1;
Name = "fred";
Value = 456;
}
}
}
Mapper.CreateMap<Test1, Test2>();
Mapper.CreateMap<Test1Child, Test2Child>()
.ConstructUsing(t => t.ChildId > 0 ? new Child(t.ChildId) : new Child())
.ForMember(m => m.Name, o => o.Ignore());
Mapper.AssertConfigurationIsValid();
var c = new Test1 { Children = new List<Test1Child> { new Test1Child { ChildId = 1, Value = 123 } } };
var d = new Test2 { Children = new List<Test2Child> { new Test2Child { ChildId = 1, Name = "fred", Value = 456 } } };
Mapper.Map(c, d);
Assert.AreEqual(d.Children[0].Value, 123);
Assert.AreEqual(d.Children[0].Name, "fred");