使用自定义解析程序跳过空值

我想使用automapper在我的公共数据协定和我的数据库模型之间进行映射。 我创建了一个自动完成所有契约的类创建映射。 我唯一的问题是,如果值不为null,我只想将合同中的值映射到数据库模型。 我在这里看了其他问题但是看不到使用自定义解析器的示例。

这是我的一些代码

var mapToTarget = AutoMapper.Mapper.CreateMap(contract, mappedTo); foreach (var property in contract.GetProperties().Where(property => property.CustomAttributes.Any(a => a.AttributeType == typeof(MapsToProperty)))) { var attribute = property.GetCustomAttributes(typeof(MapsToProperty), true).FirstOrDefault() as MapsToProperty; if (attribute == null) continue; mapToTarget.ForMember(attribute.MappedToName, opt => opt.ResolveUsing() .ConstructedBy(() => new ContractToSourceResolver(new MapsToProperty(property.Name, attribute.SourceToContractMethod, attribute.ContractToSourceMethod)))); } private class ContractToSourceResolver : ValueResolver { private MapsToProperty Property { get; set; } public ContractToSourceResolver(MapsToProperty property) { Property = property; } protected override object ResolveCore(IDataContract contract) { object result = null; if (Property.ContractToSourceMethod != null) { var method = contract.GetType() .GetMethod(Property.ContractToSourceMethod, BindingFlags.Public | BindingFlags.Static); result = method != null ? method.Invoke(null, new object[] {contract}) : null; } else { var property = contract.GetType().GetProperties().FirstOrDefault(p => p.Name == Property.MappedToName); if (property != null) { result = property.GetValue(contract); } } return result; } } 

这就是我想要使用它的方式

 AutoMapper.Mapper.Map(dataContract, dbModel) 

这当前工作得很好但是如果dataContract中有一个NULL值,那么它将替换dbModel中的现有值,这不是我想要的。 如何使AutoMapper忽略空源值?

编辑

正如其中一个答案所指出的那样

 Mapper.CreateMap().ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull)); 

这是理想的,除了.ForAllMembers无法访问的事实

 Mapper.CreateMap(SourceType, DestinationType) 

更新: 从V5开始, IsSourceValueNull 不可用 。

如果您希望忽略具有空值的所有源属性,则可以使用:

 Mapper.CreateMap() .ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull)); 

否则,您可以为每个成员执行类似的操作。

读这个 。

对于使用Instance API的较新版本,请改用:

 var mappingConfig = new MapperConfiguration(cfg => { cfg.CreateMap().ForAllMembers(opt => opt.Condition( (source, destination, sourceMember, destMember) => (sourceMember != null))); }); 

注意:此function从5.0.2开始工作,在撰写本文时打破了更高版本。 如果从5.0.2升级,建议等待下一个5.2.x版本。

此解决方案适用于我的项目,即使用AutoMapper 6.0.2。 在以前使用AutoMapper 4的项目中,我使用了IsSourceValueNull来实现类似的行为。 我在项目中将可空类型映射到非可空类型,此解决方案能够处理该场景。

我对原始解决方案做了一些小改动。 我没有检查要映射的属性的类型,而是在ForAllPropertyMaps中设置filter以检查源对象的类型,以便自定义解析器仅应用于该源对象的映射。 但是filter可以根据需要设置为任何东西。

 var config = new MapperConfiguration(cfg => { cfg.ForAllPropertyMaps( pm => pm.TypeMap.SourceType == typeof(), (pm, c) => c.ResolveUsing(new IgnoreNullResolver(), pm.SourceMember.Name)); }); class IgnoreNullResolver : IMemberValueResolver { public object Resolve(object source, object destination, object sourceMember, object destinationMember, ResolutionContext context) { return sourceMember ?? destinationMember; } } 

我将条件映射到非generics类型时遇到了同样的问题。 这是我解决它的方式。

接线:

 foreach (PropertyInfo p in type.GetProperties().Where(x => x.GetCustomAttributes().Any())) map.ForMember(p.Name, x => x.ResolveUsing(typeof(SkipMapIfNullResolver)).FromMember(p.Name)); 

第二个.FromMember是必需的,因此成员的值将传递给值解析器,而不是完整模型。

解析器看起来像这样:

 public class SkipMapIfNullResolver : IValueResolver { public ResolutionResult Resolve(ResolutionResult source) { if (source.Value == null) source.ShouldIgnore = true; return source; } }