AutoMapper:“忽略其余的”?

时间:2009-06-05 06:18:37

标签: .net automapper

有没有办法告诉AutoMapper忽略除明确映射的属性之外的所有属性?

我有外部DTO类可能会从外部更改,我想避免指定要显式忽略的每个属性,因为添加新属性会在尝试将它们映射到我自己的对象时破坏功能(导致异常)

19 个答案:

答案 0 :(得分:217)

我已将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();

答案 1 :(得分:184)

根据我的理解,问题是目标上的字段在源中没有映射字段,这就是为什么要查找忽略那些非映射目标字段的方法。

您可以简单地使用

,而不是实现和使用这些扩展方法
Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Source);  

现在,automapper知道它只需要验证所有源字段是否已映射,而不是相反。

您也可以使用:

Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Destination);  

答案 2 :(得分:82)

我已经能够通过以下方式实现这一目标:

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。

答案 3 :(得分:77)

这是我编写的一种扩展方法,它忽略了目标上的所有非现有属性。不确定它是否仍然有用,因为问题已经超过两年了,但我遇到了同样的问题,不得不添加大量的手动忽略调用。

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();

UPDATE :如果您有自定义映射,显然这不能正常工作,因为它会覆盖它们。我想如果先调用IgnoreAllNonExisting然后再调用自定义映射,它仍然可以工作。

schdr有一个解决方案(作为这个问题的答案),它使用Mapper.GetAllTypeMaps()找出哪些属性未映射并自动忽略它们。对我来说似乎是一个更强大的解决方案。

答案 4 :(得分:53)

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以便在这个类的更改频率稳定后再回来并明确这些成员是明智的。

答案 5 :(得分:42)

自AutoMapper 5.0起,.TypeMap上的IMappingExpression属性消失了,这意味着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));
    }
}

答案 6 :(得分:17)

问题问题已经有几年了,但是使用当前版本的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;
}

答案 7 :(得分:16)

对于在4.2.0及更高版本中使用non-static API的用户,可以使用以下扩展方法(在AutoMapperExtensions类中找到here):

// 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字段。

答案 8 :(得分:15)

对于Automapper 5.0,为了跳过所有未映射的属性,只需要输入

.ForAllOtherMembers(X =&GT; 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字段,其他所有字段都将被跳过。像魅力一样,似乎我们不再需要任何棘手的扩展了!

答案 9 :(得分:10)

我更新了Robert Schroeder对AutoMapper 4.2的回答。使用非静态映射器配置时,我们无法使用Mapper.GetAllTypeMaps(),但expression会引用所需的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;
}

答案 10 :(得分:8)

您希望如何指定忽略某些成员?是否有您想要应用的约定,基类或属性?一旦你开始明确指定所有映射的业务,我不确定你从AutoMapper中得到什么价值。

答案 11 :(得分:7)

这似乎是个老问题,但我想我会把答案发给其他看起来像我的人。

我使用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());

答案 12 :(得分:4)

默认情况下,AutoMapper 使用目标类型来验证成员,但您可以使用 MemberList.None 选项跳过验证。

var configuration = new MapperConfiguration(cfg =>
  cfg.CreateMap<Source2, Destination2>(MemberList.None);
);

您可以找到参考 here

答案 13 :(得分:1)

关于忽略许多成员的唯一信息就是这个主题 - http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641f。我认为您可以使用ProvideCommonBaseClassConfiguration中使用的技巧来忽略类似类的公共属性 并且没有关于“忽略其余”功能的信息。我之前看过代码,在我看来,添加这样的功能将非常非常困难。您还可以尝试使用某个属性并使用忽略的属性进行标记,并添加一些通用/公共代码以忽略所有标记的属性。

答案 14 :(得分:0)

您可以使用ForAllMembers,而不仅仅需要覆盖 像这样

public static IMappingExpression<TSource, TDest> IgnoreAll<TSource, TDest>(this IMappingExpression<TSource, TDest> expression)
        {
            expression.ForAllMembers(opt => opt.Ignore());
            return expression;
        }

小心,它会忽略所有,如果你不添加自定义映射,它们已被忽略并且无法正常工作

另外,我想说,如果你有AutoMapper的单元测试。并且您测试所有属性都已正确映射的模型,您不应使用此类扩展方法

你应该明确地写下忽略

答案 15 :(得分:0)

我知道这是一个老问题,但@jmoerdyk 在你的问题中:

  

如何在配置文件中的链式CreateMap()表达式中使用它?

你可以在Profile ctor

中使用这个answer
this.IgnoreUnmapped();
CreateMap<TSource, Tdestination>(MemberList.Destination)
.ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));

答案 16 :(得分:0)

在用于 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);

答案 17 :(得分:-1)

当前(版本9)用于忽略目标类型中不存在的属性的解决方案是创建一个翻转的映射并将其反转:

var config = new MapperConfiguration(cfg => {
  cfg.CreateMap<TheActualDestinationType, TheActualSourceType>().ReverseMap();
});

答案 18 :(得分:-2)

在3.3.1版本中,您只需使用IgnoreAllPropertiesWithAnInaccessibleSetter()IgnoreAllSourcePropertiesWithAnInaccessibleSetter()方法。