web-dev-qa-db-ja.com

Automapper 6でのマッピング中にすべてのソースメンバーのnull値を無視する方法は?

私はどこでも探していました:stackoverflow、automapper documentation、internets、これに関する情報を見つけることができませんでしたが、これも非常に一般的な問題のようです。

私のマッピング:

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

これは、srcがソースプロパティではなくソースオブジェクト(StatusLevelDTO)を表すためだと思います(私は思う)。

具体的には、ObjectAをObjectBにマッピングし、ObjectA.SomeValueがnullでObjectB.SomeValueが2の場合、宛先オブジェクトにその値(2)を保持させます。

私はこの質問を見ました: Automapperはカスタムリゾルバでnull値をスキップします と最初の2つの答えを試しましたが、両方ともバージョン6では古いようです。

Automapper 6でこれを実現する方法はありますか?正確には6.0.2を使用しています。

28
Sikor

メソッド Condition には5つのオーバーロードがあり、そのうちの1つは型の述語を受け入れます

Func<TSource, TDestination, TMember, bool>

このTMemberパラメーターはソースメンバーです。したがって、nullのソースメンバーを確認できます。

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

ソリューション 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;
    }
}
6
Tim

コメントする評判がないので、@ Sikor @senseiの回答をここに追加します。

DTOのNull許容データ型を持つモデルを使用している場合、以下のこの拡張メソッドを使用して、特定のデータ型のデフォルト値に依存する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値のみを変更します。

1
tw1tch01

私は@Sergey Berezovskiyの回答、およびメイン構成のすべてのマップのすべてのメンバーに対してこの構成を行いました。

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