【问题标题】:How to ignore null values for all source members during mapping in Automapper 6?在 Automapper 6 中映射期间如何忽略所有源成员的空值?
【发布时间】:2017-10-12 08:22:10
【问题描述】:

我到处寻找:stackoverflow、automapper 文档、互联网,但找不到任何关于此的信息,即使这似乎是一个非常常见的问题。

我的映射:

CreateMap<StatusLevelDTO, StatusLevel>()
            .ForAllMembers(opt => opt.Condition(src => src != null));

这不起作用,因为 src 代表源对象(StatusLevelDTO),而不是源属性(我认为)。

更具体地说,如果我将 ObjectA 映射到 ObjectB,ObjectA.SomeValue 为 null 而 ObjectB.SomeValue 为 2,我希望目标对象保持其值 (2)。

我已经看到了这个问题:Automapper skip null values with custom resolver 并尝试了前两个答案,但对于版本 6,它们似乎都已过时。

有没有办法在 Automapper 6 中实现这一点?确切地说,我使用的是 6.0.2。

【问题讨论】:

    标签: c# mapping automapper


    【解决方案1】:

    方法Condition 现在有五个重载,其中一个接受类型的谓词

    Func<TSource, TDestination, TMember, bool>
    

    这个 TMember 参数是源成员。所以你可以检查源成员是否为空:

    CreateMap<StatusLevelDTO, StatusLevel>()
         .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
    

    【讨论】:

    • 实际上,在发布此问题之前,我已经尝试过类似的事情。唯一的区别是我使用了 4 个参数,如下所示:(src, dest, srcMember, dstMember) 但它不起作用。我再次检查了您的版本,但仍然无法正常工作。
    • 这很有趣。当我调用 Mapper.Map(updatedStatusLevel, level); 目标对象中的 2 个字段时,它们不应该发生变化。一个从 2 更改为 0(int 类型),另一个从具有 4 个条目的列表更改为 null,因为这两个字段在源对象中均为 null。
    • 是的,看起来它对你有用。我认为唯一的区别是我正在调试单元测试。我将再次尝试运行整个应用程序。也许测试有问题。
    • @Sikor 似乎您在源对象中有int?,在目标中有int。当 AutoMapper 尝试映射此字段时,它会检查目标是否可以为空,并使用默认 int 值作为 sourceMember 值。这就是为什么空检查条件失败并且将此默认值分配给目标对象的原因
    • 这很好用,但似乎不适用于ReverseMap()
    【解决方案2】:

    这可能会迟到,但对于那些仍在寻找的人来说,这可能会解决你和我一样的问题。

    我同意@sergey 使用:

    CreateMap<StatusLevelDTO, StatusLevel>()
        .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
    

    但是将 nullable 映射到 non nullable 将是一个类似 int 的问题?到 int 它总是返回 0。要修复它,你可以转换 int 吗?在映射中转换为 int。

    CreateMap<int?, int>().ConvertUsing((src, dest) => src ?? dest);
    CreateMap<StatusLevelDTO, StatusLevel>()
         .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
    

    【讨论】:

    • 完美。谢谢!
    • 救命稻草???
    • 这太棒了。谢谢!
    【解决方案3】:

    解决方案here 适用于我的项目,该项目使用 AutoMapper 6.0.2。在之前使用 AutoMapper 4 的项目中,我使用 IsSourceValueNull 来实现相同的行为。

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

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

    【讨论】:

    • 需要进行一些更改才能在 AutoMapper 8.1.1 中使用:cfg.ForAllPropertyMaps(pm => pm.TypeMap.SourceType == typeof(), (pm, c) => c.MapFrom(new IgnoreNullResolver(), pm.SourceMember.Name))
    【解决方案4】:

    我从@Sergey Berezovskiy 的回答中获得灵感,并在主配置中为所有地图的所有成员进行了此配置:

    Mapper.Initialize(cfg =>
    {
      cfg.ForAllMaps((obj, cnfg) => cnfg.ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null)));
    }
    

    【讨论】:

      【解决方案5】:

      由于我没有评论的声誉,我将在这里为@Sikor @sensei 添加我的答案

      如果您使用的模型具有 DTO 的可为空数据类型,则可以使用下面的此扩展方法来否定 Automapper 诉诸特定数据类型的默认值的影响。

      模型示例

      public class Foo {
          public bool? Example { get; set; }
      }
      
      public class FooDto {
          public bool Example { get; set; }
      }
      

      扩展方法:

      public static TTarget MapModelProperties<TTarget, TSource>(this TTarget target, TSource source) where TTarget : class
                                                                                                      where TSource : class
      
          {
              // Map target into the source, where the source property is null
              Mapper.Initialize(cfg =>
              {
                  cfg.CreateMap<TTarget, TSource>()
                      .ForAllMembers(opt => opt.Condition((src, dest, srcMember, destMember) => destMember == null));
              });
              Mapper.Map(target, source);
      
              // Map the source into the target to apply the changes
              Mapper.Initialize(cfg => cfg.CreateMap<TSource, TTarget>());
              Mapper.Map(source, target);
      
              return target;
          }
      

      用法

      public class Foo
      {
          public bool? Example { get; set; }
      }
      
      public class FooDto
      {
          public bool Example { get; set; }
      }
      
      public void Example()
      {
          var foo = new Foo
          {
              Example = null
          };
      
          var fooDto = new FooDto
          {
              Example = true
          };
      
          fooDto.MapModelProperties(foo);
      }
      

      这会将 Dto 属性值映射到所有模型的 null 属性值。然后将模型属性值映射回 Dto,从而仅更改模型中存在的 Dto 值。

      【讨论】:

      • 你解决了吗?我有同样的问题
      猜你喜欢
      • 2023-01-26
      • 1970-01-01
      • 1970-01-01
      • 2011-05-21
      • 2017-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-16
      相关资源
      最近更新 更多