有没有办法告诉 AutoMapper 忽略除显式映射的属性之外的所有属性?
我有可能从外部更改的外部 DTO 类,我想避免明确指定要忽略的每个属性,因为添加新属性会在尝试将它们映射到我自己的对象时破坏功能(导致异常)。
有没有办法告诉 AutoMapper 忽略除显式映射的属性之外的所有属性?
我有可能从外部更改的外部 DTO 类,我想避免明确指定要忽略的每个属性,因为添加新属性会在尝试将它们映射到我自己的对象时破坏功能(导致异常)。
据我了解,问题是目标上的某些字段在源中没有映射字段,这就是为什么您正在寻找忽略那些非映射目标字段的方法。
而不是实现和使用这些扩展方法,您可以简单地使用
Mapper.CreateMap<sourceModel, destinationModel>(MemberList.Source);
现在自动映射器知道它只需要验证所有源字段都已映射,而不是相反。
您还可以使用:
Mapper.CreateMap<sourceModel, destinationModel>(MemberList.Destination);
我已经更新了 Can Gencer 的扩展以不覆盖任何现有的地图。
public static IMappingExpression<TSource, TDestination>
IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof (TSource);
var destinationType = typeof (TDestination);
var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType.Equals(sourceType) && x.DestinationType.Equals(destinationType));
foreach (var property in existingMaps.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
用法:
Mapper.CreateMap<SourceType, DestinationType>()
.ForMember(prop => x.Property, opt => opt.MapFrom(src => src.OtherProperty))
.IgnoreAllNonExisting();
AutoMapper 5.0.0-beta-1 版本引入了ForAllOtherMembers
扩展方法,因此您现在可以这样做:
CreateMap<Source, Destination>()
.ForMember(d => d.Text, o => o.MapFrom(s => s.Name))
.ForMember(d => d.Value, o => o.MapFrom(s => s.Id))
.ForAllOtherMembers(opts => opts.Ignore());
请注意,显式映射每个属性有一个优势,因为当您忘记映射属性时,您永远不会遇到映射失败的问题。
TODO
也许在您的情况下,忽略所有其他成员并添加 a以在此类更改的频率稳定下来后返回并明确说明这些可能是明智之举。
这是我编写的一个扩展方法,它忽略了目标上所有不存在的属性。不确定它是否仍然有用,因为这个问题已经超过两年了,但我遇到了同样的问题,不得不添加很多手动忽略调用。
public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>
(this IMappingExpression<TSource, TDestination> expression)
{
var flags = BindingFlags.Public | BindingFlags.Instance;
var sourceType = typeof (TSource);
var destinationProperties = typeof (TDestination).GetProperties(flags);
foreach (var property in destinationProperties)
{
if (sourceType.GetProperty(property.Name, flags) == null)
{
expression.ForMember(property.Name, opt => opt.Ignore());
}
}
return expression;
}
用法:
Mapper.CreateMap<SourceType, DestinationType>()
.IgnoreAllNonExisting();
更新:显然,如果您有自定义映射,这将无法正常工作,因为它会覆盖它们。我想如果先调用 IgnoreAllNonExisting 然后再调用自定义映射,它仍然可以工作。
schdr 有一个解决方案(作为这个问题的答案),用于Mapper.GetAllTypeMaps()
找出哪些属性未映射并自动忽略它们。对我来说似乎是一个更强大的解决方案。
我已经能够通过以下方式做到这一点:
Mapper.CreateMap<SourceType, DestinationType>().ForAllMembers(opt => opt.Ignore());
Mapper.CreateMap<SourceType, DestinationType>().ForMember(/*Do explicit mapping 1 here*/);
Mapper.CreateMap<SourceType, DestinationType>().ForMember(/*Do explicit mapping 2 here*/);
...
注意:我使用的是 AutoMapper v.2.0。
从 AutoMapper 5.0 开始,.TypeMap
属性 onIMappingExpression
消失了,这意味着 4.2 解决方案不再有效。我创建了一个使用原始功能但语法不同的解决方案:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Src, Dest>();
cfg.IgnoreUnmapped(); // Ignores unmapped properties on all maps
cfg.IgnoreUnmapped<Src, Dest>(); // Ignores unmapped properties on specific map
});
// or add inside a profile
public class MyProfile : Profile
{
this.IgnoreUnmapped();
CreateMap<MyType1, MyType2>();
}
执行:
public static class MapperExtensions
{
private static void IgnoreUnmappedProperties(TypeMap map, IMappingExpression expr)
{
foreach (string propName in map.GetUnmappedPropertyNames())
{
if (map.SourceType.GetProperty(propName) != null)
{
expr.ForSourceMember(propName, opt => opt.Ignore());
}
if (map.DestinationType.GetProperty(propName) != null)
{
expr.ForMember(propName, opt => opt.Ignore());
}
}
}
public static void IgnoreUnmapped(this IProfileExpression profile)
{
profile.ForAllMaps(IgnoreUnmappedProperties);
}
public static void IgnoreUnmapped(this IProfileExpression profile, Func<TypeMap, bool> filter)
{
profile.ForAllMaps((map, expr) =>
{
if (filter(map))
{
IgnoreUnmappedProperties(map, expr);
}
});
}
public static void IgnoreUnmapped(this IProfileExpression profile, Type src, Type dest)
{
profile.IgnoreUnmapped((TypeMap map) => map.SourceType == src && map.DestinationType == dest);
}
public static void IgnoreUnmapped<TSrc, TDest>(this IProfileExpression profile)
{
profile.IgnoreUnmapped(typeof(TSrc), typeof(TDest));
}
}
问这个问题已经有几年了,但这种扩展方法对我来说似乎更干净,使用当前版本的 AutoMapper (3.2.1):
public static IMappingExpression<TSource, TDestination> IgnoreUnmappedProperties<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
if (typeMap != null)
{
foreach (var unmappedPropertyName in typeMap.GetUnmappedPropertyNames())
{
expression.ForMember(unmappedPropertyName, opt => opt.Ignore());
}
}
return expression;
}
对于 Automapper 5.0,为了跳过所有未映射的属性,您只需要 put
.ForAllOtherMembers(x=>x.Ignore());
在您的个人资料末尾。
例如:
internal class AccountInfoEntityToAccountDtoProfile : Profile
{
public AccountInfoEntityToAccountDtoProfile()
{
CreateMap<AccountInfoEntity, AccountDto>()
.ForMember(d => d.Id, e => e.MapFrom(s => s.BankAcctInfo.BankAcctFrom.AcctId))
.ForAllOtherMembers(x=>x.Ignore());
}
}
在这种情况下,只有输出对象的 Id 字段将被解析,所有其他字段将被跳过。像魅力一样工作,似乎我们不再需要任何棘手的扩展!
对于那些在 4.2.0 及以上版本中使用非静态 API的人,可以使用以下扩展方法(可在类中找到AutoMapperExtensions
):
// from http://stackoverflow.com/questions/954480/automapper-ignore-the-rest/6474397#6474397
public static IMappingExpression IgnoreAllNonExisting(this IMappingExpression expression)
{
foreach(var property in expression.TypeMap.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
这里重要的是,一旦删除了静态 API,诸如此类的代码Mapper.FindTypeMapFor
将不再工作,因此需要使用该expression.TypeMap
字段。
我已经更新了 Robert Schroeder 对 AutoMapper 4.2 的回答。使用非静态映射器配置,我们不能使用Mapper.GetAllTypeMaps()
,但expression
有对 required 的引用TypeMap
:
public static IMappingExpression<TSource, TDestination>
IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
foreach (var property in expression.TypeMap.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
您希望如何指定忽略某些成员?是否有您想要应用的约定、基类或属性?一旦您开始明确指定所有映射的业务,我不确定您会从 AutoMapper 中获得什么价值。
这似乎是一个老问题,但我想我会为其他看起来像我的人发布我的答案。
我使用 ConstructUsing,对象初始化器加上 ForAllMembers 忽略例如
Mapper.CreateMap<Source, Target>()
.ConstructUsing(
f =>
new Target
{
PropVal1 = f.PropVal1,
PropObj2 = Map<PropObj2Class>(f.PropObj2),
PropVal4 = f.PropVal4
})
.ForAllMembers(a => a.Ignore());
默认情况下,AutoMapper 使用目标类型来验证成员,但您可以使用MemberList.None选项跳过验证。
var configuration = new MapperConfiguration(cfg =>
cfg.CreateMap<Source2, Destination2>(MemberList.None);
);
你可以在这里找到参考
关于忽略许多成员的唯一信息是这个线程 - http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f。我认为您可以使用提供CommonBaseClassConfiguration 中使用的技巧来忽略类似类的公共属性。
并且没有关于“忽略其余”功能的信息。我之前看过代码,在我看来,添加这样的功能将非常非常困难。您也可以尝试使用一些属性并用它标记忽略的属性,并添加一些通用/通用代码来忽略所有标记的属性。
我知道这是一个老问题,但@jmoerdyk 在你的问题中:
您将如何在 Profile 的链式 CreateMap() 表达式中使用它?
您可以在 Profile ctor 中像这样使用这个答案
this.IgnoreUnmapped();
CreateMap<TSource, Tdestination>(MemberList.Destination)
.ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));
在 dotnet 5 的 WebApi 中,使用 Nuget 包AutoMapper.Extensions.Microsoft.DependencyInjection
,我在映射器配置文件中这样做。我对 AutoMapper 真的很生疏,但现在对于未映射的成员来说似乎工作正常。
在启动中:
var mapperConfig = new MapperConfiguration(mc => mc.AddProfile(new AutoMapperProfile()));
services.AddSingleton(mapperConfig.CreateMapper());
在我的 AutoMapperProfile 中:
CreateMap<ProjectActivity, Activity>()
.ForMember(dest => dest.ActivityName, opt => opt.MapFrom(src => src.Name))
.ValidateMemberList(MemberList.None);
您可以使用 ForAllMembers,而不是像这样只需要覆盖
public static IMappingExpression<TSource, TDest> IgnoreAll<TSource, TDest>(this IMappingExpression<TSource, TDest> expression)
{
expression.ForAllMembers(opt => opt.Ignore());
return expression;
}
小心,它会忽略所有,如果你不添加自定义映射,它们已经被忽略并且不起作用
另外,我想说,如果你有 AutoMapper 的单元测试。并且您测试所有具有正确映射所有属性的模型,您不应该使用这种扩展方法
你应该明确写忽略
忽略目标类型中不存在的属性的当前(版本 9)解决方案是创建翻转映射并将其反转:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<TheActualDestinationType, TheActualSourceType>().ReverseMap();
});
在 3.3.1 版本中,您可以简单地使用IgnoreAllPropertiesWithAnInaccessibleSetter()
orIgnoreAllSourcePropertiesWithAnInaccessibleSetter()
方法。