【问题标题】:AutoMapper: "Ignore the rest"?AutoMapper:“忽略其余部分”?
【发布时间】:2010-10-31 13:37:36
【问题描述】:

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

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

【问题讨论】:

  • 使用 ValueInjecter valueinjecter.codeplex.com/documentation 创建具有映射算法并在特定属性之间映射的 ValueInjections,它们不关心其余属性
  • 对于那些使用 Automapper > 版本 5 的用户,请跳至查看详细答案.ForAllOtherMembers(opts => opts.Ignore())
  • @Schneider ".ForAllOtherMembers(opts => opts.Ignore())" 与此处的扩展名 "IgnoreAllNonExisting" 不同,主要区别在于如果您没有显式配置属性,则使用 ".ForAllOtherMembers (opts => opts.Ignore())" 你不会得到任何映射。在没有明确配置属性的情况下使用“IgnoreAllNonExisting”,您仍然会得到一些属性映射(具有相同名称的属性)的值。
  • 是的。 ForAllOtherMembers 就是答案。 IgnoreUnmapped 答案除了导致 config-valid-assert 通过之外什么都不做,因为无论如何都会忽略未映射的成员。
  • 值得注意的是,这样做时,您明确隐藏了正在映射的类中可能相关或重要的更改。每当映射的类发生变化时,对每个属性进行显式映射都会使您的测试中断,从而迫使您正确评估它。 (假设您有一个测试正在执行AssertConfigurationIsValid() 调用)因此,我认为“忽略其余部分”是一种反模式。

标签: .net automapper


【解决方案1】:

据我了解,问题是目标上的某些字段在源中没有映射字段,这就是为什么您正在寻找忽略那些非映射目标字段的方法。

您可以简单地使用

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

现在自动映射器知道它只需要验证所有源字段是否都已映射,反之则不需要。

你也可以使用:

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

【讨论】:

  • 这个答案应该有更多的赞成票,甚至可能被标记为答案。它解决了我的问题,同样MemberList.Destination 将解决操作问题。
  • 如果您想忽略源和目标上的一些属性,它将无法工作:)
  • 对于后来的人来说,这是 5.0 的正确答案
  • 看起来很漂亮,但对我不起作用..我尝试了 Source 和 Destination,但它一直抱怨同一个属性对象缺少地图
  • 使用 6.0.2,这不起作用。任何未从目标映射到源的属性,用空值和 0 覆盖源中的属性。另外,代码并不能清楚地说明您在做什么,尤其是当您在团队中工作时。这就是为什么我非常不喜欢这段代码,以及为什么我更喜欢选择词,比如建议的答案“IgnoreAllNonExisting”
【解决方案2】:

我已更新 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,感谢您发布此解决方案。当我在goo.gl/rG7SL 中使用解决方案时,我花了好几个小时才发现奇怪的错误,直到我再次偶然发现这篇文章。
  • 我在下面推荐 Yohanb 的方法。在某些极端情况下,这不起作用。
  • 这可以在 AutoMapper 4.2 中完成吗? (Mapper.GetAllTypeMaps() 已弃用)
  • 对于 AutoMapper 5+ 版本,只需将 Mapper.GetAllTypeMaps() 替换为 Mapper.Configuration.GetAllTypeMaps()。这里是参考github.com/AutoMapper/AutoMapper/issues/1252
  • 适合阅读本文的新人。此答案适用于 AutoMapper 2,在撰写此评论时,我们处于第 6 版。这是一个 hack,更简洁的方法是使用 MemberList 枚举。请参阅 Github 问题 1839 和更好的解决方案。 github.com/AutoMapper/AutoMapper/issues/1839 例如:stackoverflow.com/a/31182390/3850405
【解决方案3】:

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。看看有多少赞成票和尝试回答这个问题......我想知道 Automapper 的治理有问题吗?
  • 谢谢你,我花了一段时间才向下滚动到它,但它工作得很好。
  • 您甚至可以将 ForAllOtherMembers 行放在首位,一切都一样,如果您有某种基类配置,那就太好了。
  • 是否有忽略源对象中的属性的等价物? ForAllOtherSourceMembers 之类的东西?
  • 即使它回答了这个问题,Jimmy Bogard explained ForAllOtherMembers(opts => opts.Ignore()) 也违背了 Automapper 的目的。考虑使用 IgnoreUnmapped<Src, Dest>() 仍然按约定映射成员,并避免来自 AssertConfigurationIsValid() 的警报
【解决方案4】:

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

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() 找出哪些属性未映射并自动忽略它们。对我来说似乎是一个更强大的解决方案。

【讨论】:

  • 我已经有一段时间没有使用 AutoMapper,但如果它适合你,我会接受你的回答:)。
  • 谢谢!!我发现这很方便。在我的情况下,单独忽略属性违背了使用自动映射器的目的。
  • 查看下一个没有覆盖问题的答案
  • 这个方法应该在autoMapper原生代码上!非常好,谢谢!
  • 仅供参考,Jimmy 本人(AutoMapper 的作者)在下面评论说@nazim 的答案对于版本 5+ 是正确的
【解决方案5】:

我可以通过以下方式做到这一点:

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。

【讨论】:

  • 非常感谢!它就像一个魅力。我首先尝试链接调用,但 ForAllMembers 只返回 void :(。前面的 IgnoreAll 可以稍后修改并不明显。
  • 我也不喜欢这种方式..如果你有 50 个成员,并且你想忽略 25..那么如果你仍然要忽略 25 个成员,那么 automapper 的意义何在。如果名称匹配,并且存在不匹配的属性.. 为什么不明确告诉 automapper 在未映射的属性上不匹配并传递所有类型?
【解决方案6】:

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

【讨论】:

  • 你将如何在Profile 的链式CreateMap&lt;TSource,TDest&gt;() 表达式中使用它?
  • 谢谢。 GetUnmappedPropertyNames 方法返回源和目标上所有未映射的属性名称,这似乎在反向映射上中断,因此我必须对 IgnoreUnmapped 进行小幅更改以检查未映射的属性是否在源或目标上并忽略因此。这是一个演示问题和更新的小提琴:dotnetfiddle.net/vkRGJv
  • 我已经更新了我的答案以包含您的发现 - 我不使用源映射所以没有遇到这个!谢谢。
  • 这在没有可用反射的 PCL 上不起作用,GetProperty(propName) 不存在。
  • 我不明白这是如何解决这个问题的,或者这甚至是如何做的。未映射的属性将被忽略 - 因为它们是未映射的。海报说“除非它们被明确映射,否则你如何忽略道具”。这意味着,如果我有 Src.MyProp 和 Dest.MyProp,则应该忽略该映射,除非有对 MyProp 的 MapFrom 和 ForMember 的显式调用。因此,需要忽略默认映射。该解决方案唯一要做的就是使 config-valid-assert 事情通过 - 无论如何您都不需要它来使映射工作。
【解决方案7】:

问这个问题已经有几年了,但这种扩展方法对我来说似乎更干净,使用当前版本的 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;
}

【讨论】:

    【解决方案8】:

    对于 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 字段将被解析,所有其他将被跳过。像魅力一样工作,似乎我们不再需要任何棘手的扩展!

    【讨论】:

    • 这真的有效吗?使用这种方法我仍然得到所有其他成员并设置为默认值......不是原始值,即使使用 x=>x.UseDestinationValue()
    【解决方案9】:

    对于在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 字段。

    【讨论】:

    • 从 5.0 开始,expression.TypeMap 不再可用。这是my solution for 5.0
    • 我不得不使用public static IMappingExpression&lt;TSource, TDestination&gt; IgnoreAllNonExisting&lt;TSource, TDestination&gt;(this IMappingExpression&lt;TSource, TDestination&gt; expression) 来修复类型问题。
    【解决方案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;
    }
    

    【讨论】:

    • 在 AutoMapper 5.0 中不起作用。 IMappingExpression 上的 .TypeMap 属性不可用。对于 5.+ 版本,请参阅 Richard's answer 中的扩展
    • 适用于 AM 4.2
    【解决方案11】:

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

    【讨论】:

    • 吉米,你有关于明确的观点。至于如何以最优雅的方式实现这一点:基类和属性在这种情况下不起作用,因为目标类并没有真正在我的控制之下——它们是从 XSD 数据协定自动生成的,所以人们会有在每个生成周期后手动编辑此代码。我想解决方案取决于具体案例。也许类似于 Windsor Castle 提供的用于选择在容器中注册哪些组件的流畅界面可能是一种解决方案?
    • 啊,现在更有意义了。这是一个有趣的功能,我会在 2.1 的时间范围内看看它。
    • 拥有一个可以“忽略”所有不存在的字段的可配置值怎么样。
    • 这不是问题的答案。
    • 嗨吉米,你是作者,对吗?我希望能够忽略所有不存在的属性作为默认行为(可能由标志控制)。另外,我无法弄清楚 AutoMapper 的一个奇怪错误。它没有给我任何细节。
    【解决方案12】:

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

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

    【讨论】:

      【解决方案13】:

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

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

      您可以找到参考here

      【讨论】:

        【解决方案14】:

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

        【讨论】:

        • 也许一种方法是使用 ForAllMembers 方法并实现我自己的 IMemberConfigurationExpression 接收包含不应忽略的那些属性的属性名称的字符串,然后遍历其余部分并调用 Ignore ()。只是一个想法,我不确定它是否可行。
        • 是的,这也可以,但是这种方法比使用属性更棘手,但它提供了更大的灵活性。可惜没有灵丹妙药:(。
        【解决方案15】:

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

        如何在 Profile 的链式 CreateMap() 表达式中使用它?

        您可以在 Profile ctor 中像这样使用 answer

        this.IgnoreUnmapped();
        CreateMap<TSource, Tdestination>(MemberList.Destination)
        .ForMember(dest => dest.SomeProp, opt => opt.MapFrom(src => src.OtherProp));
        

        【讨论】:

          【解决方案16】:

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

          【讨论】:

          • 不知道.ValidateMemberList(MemberList.None).ForAllOtherMembers(x =&gt; x.Ignore())有什么区别?
          【解决方案17】:

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

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

          小心,它会忽略所有,如果你不添加自定义映射,它们已经被忽略并且不起作用

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

          你应该明确写忽略

          【讨论】:

            【解决方案18】:

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

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

            【讨论】:

              【解决方案19】:

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

              【讨论】:

              • 这不适用于原始发帖人的问题。这些方法仅忽略受保护或私有属性,而不是源中缺少但存在于目标类型中的属性。
              猜你喜欢
              • 2022-09-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多