Automapper通用映射需求:仅映射非默认值属性
用AutoMapper实现「仅映射源对象已修改属性」的通用方案
我完全理解你的需求:只有当Source对象的属性值偏离了它自身的默认值时,才将该值映射到Destination;如果源属性还是默认值,就保留Destination自己的默认值。AutoMapper本身没有直接的内置开关,但我们可以通过自定义条件判断来完美实现这个逻辑,下面给你一步步拆解:
1. 先确认我们的源和目标类(你的现有代码)
public class Source { public string Foo { get; set; } = "Foo"; public string Bar { get; set; } = "Bar"; } public class Destination { public string Foo { get; set; } = "Foo of Destination"; public string Bar { get; set; } = "Bar of Destination"; }
2. 核心:配置AutoMapper的条件映射
关键思路是:为每个属性设置映射条件——只有当源属性的值不等于它的默认值时,才执行映射操作。我们可以先创建一个Source的默认实例,用来做对比基准。
配置代码如下:
var config = new MapperConfiguration(cfg => { // 创建一个Source的默认实例,作为判断"是否修改"的基准 var defaultSource = new Source(); cfg.CreateMap<Source, Destination>() // 给Foo属性加映射条件 .ForMember(dest => dest.Foo, opt => { // 只有当源的Foo不是默认值时,才映射 opt.Condition(src => src.Foo != defaultSource.Foo); opt.MapFrom(src => src.Foo); }) // 给Bar属性加映射条件 .ForMember(dest => dest.Bar, opt => { opt.Condition(src => src.Bar != defaultSource.Bar); opt.MapFrom(src => src.Bar); }); }); // 验证配置是否正确(推荐加上,避免配置错误) config.AssertConfigurationIsValid(); // 创建Mapper实例 var _mapper = config.CreateMapper();
3. 验证你的测试用例
现在你的三个测试用例都能完美通过:
测试用例1:ChangeAllProperties(源对象全默认)
当Source是默认状态时,所有属性都不会触发映射,Destination保留自己的默认值:
[TestMethod] public void ChangeAllProperties() { var source = new Source(); var destination = _mapper.Map<Destination>(source); var defaultDestination = new Destination(); Assert.AreEqual(destination.Bar, defaultDestination.Bar); // 结果:目标默认值"Bar of Destination" Assert.AreEqual(destination.Foo, defaultDestination.Foo); // 结果:目标默认值"Foo of Destination" }
测试用例2:ChangeDefaultProperty(仅修改Bar)
只有Bar被修改了,所以只有Bar会被映射,Foo依然保留目标的默认值:
[TestMethod] public void ChangeDefaultProperty() { var source = new Source(); source.Bar = "Custom Bar!"; var destination = _mapper.Map<Destination>(source); var defaultDestination = new Destination(); Assert.AreEqual(destination.Bar, source.Bar); // 结果:源的自定义值"Custom Bar!" Assert.AreEqual(destination.Foo, defaultDestination.Foo); // 结果:目标默认值"Foo of Destination" }
测试用例3:AlLChanged(两个属性都修改)
两个属性都偏离了默认值,所以都会被映射到Destination:
[TestMethod] public void AlLChanged() { var source = new Source(); source.Foo = "Custom Foo!"; source.Bar = "Custom Bar!"; var destination = _mapper.Map<Destination>(source); Assert.AreEqual(destination.Bar, source.Bar); // 结果:"Custom Bar!" Assert.AreEqual(destination.Foo, source.Foo); // 结果:"Custom Foo!" }
4. 进阶:通用化处理(避免逐个配置属性)
如果你的类有很多属性,一个个写ForMember太繁琐,可以用反射自动为所有属性添加条件:
var config = new MapperConfiguration(cfg => { var defaultSource = Activator.CreateInstance<Source>(); var sourceProps = typeof(Source).GetProperties(BindingFlags.Public | BindingFlags.Instance); cfg.CreateMap<Source, Destination>() .ForAllMembers(opt => { // 找到当前属性在Source中的对应属性 var sourceProp = sourceProps.First(p => p.Name == opt.DestinationMember.Name); // 获取该属性的默认值 var defaultValue = sourceProp.GetValue(defaultSource); // 设置条件:只有当源属性值不等于默认值时才映射 opt.Condition((src, dest, srcValue) => !Equals(srcValue, defaultValue)); }); });
这个方案会自动遍历所有属性,统一添加映射条件,适合属性较多的场景。
内容的提问来源于stack exchange,提问作者oiBio
相关产品推荐
相关产品推荐

