AutoMapper:"忽略其余的"?

Igo*_*ejc 196 .net automapper

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

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

Rob*_*der 222

我已经更新了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;
}
Run Code Online (Sandbox Code Playgroud)

用法:

Mapper.CreateMap<SourceType, DestinationType>()
                .ForMember(prop => x.Property, opt => opt.MapFrom(src => src.OtherProperty))
                .IgnoreAllNonExisting();
Run Code Online (Sandbox Code Playgroud)

  • 对于AutoMapper 5+版本,只需将`Mapper.GetAllTypeMaps()`替换为`Mapper.Configuration.GetAllTypeMaps()`.这是参考github.com/AutoMapper/AutoMapper/issues/1252 (14认同)
  • 对于阅读此内容的新人.这个答案适用于AutoMapper 2,在编写本评论时,我们的版本为6.这是一个黑客,更简洁的方法是使用MemberList枚举.请参阅Github问题1839和更好的解决方案.例如:https://stithoverflow.com/a/31182390 (5认同)
  • +1,感谢您发布此解决方案.当我在http://goo.gl/rG7SL中使用解决方案时,花了我几个小时来计算奇怪的错误,直到我再次偶然发现这篇文章. (4认同)
  • 我推荐Yohanb的方法.有一些极端情况,这似乎不起作用. (3认同)
  • 可以在AutoMapper 4.2中完成吗?(不推荐使用`Mapper.GetAllTypeMaps()`) (3认同)
  • 有道理,似乎是一个更强大的解决方案,更新我的答案指向你的.不知道方法"GetUnmappedPropertyNames":) (2认同)
  • 适用于静态Mapper类.当您通过IoC配置AutoMapper时,您需要获取IConfigurationProvider以获取所有类型的地图. (2认同)
  • @Sergey - 当您使用新的 IMapper 方式时,这不起作用。抛出此错误:“映射器未初始化。使用适当的配置调用初始化。如果您尝试通过容器或其他方式使用映射器实例,请确保没有对静态 Mapper.Map 方法进行任何调用,并且如果您使用 ProjectTo 或 UseAsDataSource 扩展方法,请确保传入适当的 IConfigurationProvider 实例。” (2认同)

小智 218

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

您可以简单地使用,而不是实现和使用这些扩展方法

Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Source);  
Run Code Online (Sandbox Code Playgroud)

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

您还可以使用:

Mapper.CreateMap<destinationModel, sourceModel>(MemberList.Destination);  
Run Code Online (Sandbox Code Playgroud)

  • 对于后来的任何人来说,这是5.0的正确答案 (56认同)
  • 这个答案应该有更多的赞成,甚至可能被标记为答案.它解决了我的问题,同样`MemberList.Destination`将解决操作问题. (10认同)
  • 看起来很漂亮,但没有为我工作..我尝试了源和目的地,但它一直在抱怨缺少地图的相同属性对象 (3认同)
  • 使用 6.0.2 并且这不起作用。任何未从目标映射到源的属性,都使用空值和 0 覆盖源中的属性。此外,代码并没有明确说明您在做什么,尤其是当您在团队中工作时。这就是为什么我非常不喜欢这段代码,以及为什么我更喜欢像建议的答案“IgnoreAllNonExisting”这样的选择词 (3认同)
  • 如果您需要忽略额外的属性 `.ForSourceMember(src =&gt; src.Name, opt =&gt; opt.Ignore());` (2认同)

小智 83

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

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*/);
...
Run Code Online (Sandbox Code Playgroud)

注意:我使用的是AutoMapper v.2.0.

  • 非常感谢!它就像一个魅力.我首先尝试将调用链接起来,但ForAllMembers只返回void :(.之前的IgnoreAll可以稍后修改并不明显. (4认同)
  • 我也不喜欢这种方式..如果你有50个成员,并且你想忽略25 ..那么如果你仍然要忽略25个成员那么自动化的重点是什么.如果名称匹配,并且存在不匹配的属性..为什么不明确告诉automapper在未映射的属性上不匹配并通过传递所有键入? (4认同)

Can*_*cer 82

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

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;
}
Run Code Online (Sandbox Code Playgroud)

用法:

Mapper.CreateMap<SourceType, DestinationType>()
                .IgnoreAllNonExisting();
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这个方法应该在autoMapper本机代码上!很好谢谢! (3认同)
  • 谢谢!!我发现这非常方便.在我的情况下,单独忽略属性会破坏使用automapper的目的. (2认同)
  • 仅供参考,吉米本人(AutoMapper的作者)在下面评论说,@ nazim的答案对于版本5+是正确的 (2认同)

ajb*_*ven 63

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());
Run Code Online (Sandbox Code Playgroud)

请注意,显式映射每个属性都有一个优势,因为当您忘记映射属性时,您将永远不会出现静默映射失败的问题.

也许在你的情况下,忽略所有其他成员并添加一个TODO回来并在这个类的更改频率稳定下来之后使这些成员显然是明智的.

  • 这个惊人了,直到第5版看我多少了-votes并试图回答这个问题......与Automapper治理什么问题不知道? (3认同)
  • 您甚至可以将ForAllOtherMembers行放在最前面,并且一切正常,如果您具有某种基类配置,则很好。 (2认同)
  • 是否有等效的方法可以忽略源对象中的属性?像“ForAllOtherSourceMembers”之类的东西? (2认同)
  • 即使它回答了这个问题,[Jimmy Bogard 解释说](/sf/answers/66892521/) ForAllOtherMembers(opts =&gt; opts.Ignore()) 也违背了 **Auto**mapper 的目的。考虑使用 [IgnoreUnmapped&lt;Src, Dest&gt;()](/sf/answers/2665160291/) 仍按约定映射成员,并避免来自 AssertConfigurationIsValid() 的警报 (2认同)

Ric*_*ard 43

从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>();
}
Run Code Online (Sandbox Code Playgroud)

执行:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你如何在`Profile`中的链式`CreateMap <TSource,TDest>()`表达式中使用它? (3认同)
  • 谢谢你.GetUnmappedPropertyNames方法返回源和目标上的所有未映射的属性名称,这似乎是在反向映射上中断,因此我必须对IgnoreUnmapped进行一些小的更改,以检查未映射的属性是否在源或目标上并忽略因此.这是一个演示问题和更新的小提琴:https://dotnetfiddle.net/vkRGJv (2认同)

Ira*_*chi 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;
}
Run Code Online (Sandbox Code Playgroud)


nic*_*k_w 16

对于那些谁正在使用的非静态API在4.2.0及以上版本,下面的扩展方法(发现这里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;
}
Run Code Online (Sandbox Code Playgroud)

这里重要的是,一旦删除静态API,代码Mapper.FindTypeMapFor将不再起作用,因此使用该expression.TypeMap字段.

  • 从5.0开始,`expression.TypeMap`不再可用.这是[我的5.0解决方案](http://stackoverflow.com/a/38073718/163495) (7认同)

小智 16

对于Automapper 5.0,您可以跳过所有未映射的属性

.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());
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,将仅解析输出对象的Id字段,将跳过所有其他字段.像魅力一样工作,似乎我们不再需要任何棘手的扩展!


mrm*_*hal 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;
}
Run Code Online (Sandbox Code Playgroud)


Jim*_*ard 8

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

  • 这不是问题的答案. (6认同)
  • 如何只有一个可配置的值,你可以"忽略"所有不存在的字段. (2认同)

小智 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());
Run Code Online (Sandbox Code Playgroud)


小智 5

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

var configuration = new MapperConfiguration(cfg =>
  cfg.CreateMap<Source2, Destination2>(MemberList.None);
);
Run Code Online (Sandbox Code Playgroud)

你可以在这里找到参考