如何在Automapper 6中映射期间忽略所有源成员的空值?

时间:2017-05-12 22:51:40

标签: c# mapping automapper

我一直在寻找无处不在: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。

5 个答案:

答案 0 :(得分:40)

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

的谓词
Func<TSource, TDestination, TMember, bool>

此TMember参数是源成员。因此,您可以检查源成员是否为null:

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

答案 1 :(得分:5)

解决方案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;
    }
}

答案 2 :(得分:0)

我从@Sergey Berezovskiy的答案中得到启发,并在主配置中对所有地图的所有成员进行了此配置:

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

答案 3 :(得分:0)

由于我没有评论的名声,因此我将在此处添加@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值。

答案 4 :(得分:0)

这可能有点晚了,但对于那些仍在寻找的人来说,这可能会解决您和我一样的问题。

我同意@sergey 使用

<块引用>

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

但是将可空映射到不可空将是一个问题,例如:int?到 int 它总是会返回 0。要修复它,您可以转换为 int 吗?到 int 映射。

<块引用>

CreateMap().ConvertUsing((src, dest) => { if (src.HasValue) return src.Value; return dest; });
CreateMap() .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));