AutoMapper:「残りを無視する」?


206

AutoMapperに、明示的にマップされているものを除くすべてのプロパティを無視するように指示する方法はありますか?

外部から変更される可能性のある外部DTOクラスがあり、各プロパティを明示的に無視するように指定しないようにします。新しいプロパティを追加すると、自分のオブジェクトにマップしようとすると機能(例外の原因)が機能しなくなるためです。


1
ValueInjecter valueinjecter.codeplex.com/documentationを使用して、マッピングアルゴリズムを持ち、特定のプロパティ間でマッピングするValueInjectionsを作成し、残りのプロパティは気にしません
Omu

24
Automapper>バージョン5を使用している場合は、下にスキップして詳細な回答を確認してください.ForAllOtherMembers(opts => opts.Ignore())
Jack Ukleja '21

@Schneider ".ForAllOtherMembers(opts => opts.Ignore())"は、ここで拡張子 "IgnoreAllNonExisting"とは異なります。主な違いは、 "。ForAllOtherMembers(opts => opts.Ignore( )) "マップされたものは何もありません。明示的に構成プロパティなしで "IgnoreAllNonExisting"を使用しても、値を持ついくつかのプロパティがマップされます(同じ名前のプロパティ)。
Dragon

はい。ForAllOtherMembersが答えです。マッピングされていないメンバーは無視されるため、IgnoreUnmapped回答はconfig-valid-assertを通過させる以外は何もしません。
N73k 2017年

これを行うとき、マッピングされるクラスの潜在的に関連するまたは重要な変更を明示的に非表示にすることに注意する価値があります。すべてのプロパティに明示的なマッピングがあると、マップされたクラスが変更されるたびにテストが失敗し、適切に評価する必要があります。(あなたがAssertConfigurationIsValid()呼び出しを行うテストを持っていると仮定して)このため、私は「残りを無視する」ことをアンチパターンと見なします。
Arve Systad

回答:


83

これは私が書いた拡張メソッドで、宛先に存在しないすべてのプロパティを無視します。質問が2年以上前のものであるため、それがまだ役立つかどうかはわかりませんが、同じ問題に遭遇し、手動で無視する呼び出しをたくさん追加する必要がありました。

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を使用していませんが、それがあなたのために働くなら、私はあなたの答えを受け入れます:)。
Igor Brejc

2
ありがとう!! これはとても重宝しました。プロパティを個別に無視すると、私の状況でオートマッパーを使用する目的が損なわれました。
Daniel Robinson

上書きの問題がないものについては、次の回答を参照してください
Jason Coyne

3
このメソッドは、autoMapperネイティブコード上にある必要があります。ありがとう、とても良いです!
Felipe Oriani 2013

2
ちなみに、ジミー自身(AutoMapperのライター)は、@ nazimの回答がバージョン5+に対して正しいと以下でコメントしています
Worthy7

244

私が理解したことから、ソースにマップされたフィールドがない宛先にフィールドがあることが質問でした。そのため、それらのマップされていない宛先フィールドを無視する方法を探しています。

これらの拡張メソッドを実装して使用する代わりに、単に使用することができます

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

これで、オートマッパーは、すべてのソースフィールドがマップされていることを検証するだけでよく、その逆は不要であることを認識しています。

次のものも使用できます。

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

10
この回答には、より多くの投票が必要です。回答としてマークされることもあります。それは私の問題を解決し、同様にMemberList.Destinationopsの問題を解決します。
Tedd Hansen、2016

1
ソースと宛先の両方でいくつかのプロパティを無視したい場合は機能しません:)
RealWillyWoka

62
後で来る人にとって、これは5.0の正しい答えです
ジミー・ボガード

3
気の利いたように見えますが、私にとってはうまくいきませんでした。SourceとDestinationを試してみましたが、同じプロパティオブジェクトがマップを欠いていると不平を言い続けています
Sonic Soul

1
6.0.2を使用すると、これは動作しません。宛先からソースにマップされていないプロパティは、nullと0でソースのプロパティを上書きします。さらに、コードでは、特にチームで作業している場合は、何をしているのか明確になりません。それが、私がこのコードを強く嫌う理由であり、提案された回答「IgnoreAllNonExisting」のような選択語を好む理由
sksallaj

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;
}

使用法:

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

4
+1、このソリューションを投稿していただきありがとうございます。それは私が解決策を使用するときに奇妙なバグを把握するために私に時間をtooked goo.gl/rG7SL私は再びこの投稿につまずくまで、。
ノルディン

3
これよりも下のヨハンブの方法をお勧めします。これが表示されないために機能しないいくつかのまれなケースがあります。
Jon Barker

3
これはAutoMapper 4.2で実行できますか?(これMapper.GetAllTypeMaps()は非推奨です)
mrmashal 2016

14
AutoMapper 5+バージョンの場合は、に置き換えMapper.GetAllTypeMaps()てくださいMapper.Configuration.GetAllTypeMaps()。これが参照github.com/AutoMapper/AutoMapper/issues/1252
Sergey G.

5
これを読んでいる新しい人々のために。この回答はAutoMapper 2に対するものであり、このコメントの執筆時点ではバージョン6です。これはハックであり、MemberList enumを使用することで、よりクリーンな方法を実現できます。Github issue 1839とより良いソリューションを参照してください。github.com/AutoMapper/AutoMapper/issues/1839つまり、例:stackoverflow.com/a/31182390/3850405
Ogglas

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*/);
...

注:AutoMapper v.2.0を使用しています。


4
どうもありがとうございます!それは魅力のように機能します。私はちょうど、ボイド:(を返すチェーンに最初のコールが、ForAllMembersを試してみました前のIgnoreAllは後で変更することができるということは明らかではなかった。。
SeriousM

5
この方法も好きではありません。50のメンバーがいて、25を無視したい場合、25のメンバーを無視しなければならない場合のオートマッパーのポイントは何ですか。名前が一致し、一致しないプロパティがある場合は、オートマッパーにマッピングされていないプロパティと一致せず、すべての入力を渡すように指示することを明確にしないでください。
sksallaj 2018

71

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

プロパティのマッピングを忘れたときに発生するサイレントマッピングの失敗の問題が発生しないため、各プロパティを明示的にマッピングすることには利点があることに注意してください。

おそらくあなたのケースでは、他のすべてのメンバーを無視してa TODOを追加し、このクラスへの変更の頻度が落ち着いた後に戻ってこれらを明示的にするのが賢明かもしれません。


3
これはバージョン5までに驚くべきことでした。この質問に対する投票数と回答の試行数を調べてください。Automapperのガバナンスに問題があるのではないでしょうか。
Jack Ukleja

これをありがとう、下にスクロールするのに少し時間がかかりましたが、これは完全に機能します。
cobolstinks 2017

2
ForAllOtherMembers行を最初に置くこともできます。物事は同じように機能します。これは、何らかの基本クラス構成がある場合に適しています。
N73k 2017年

これが現在推奨されるアプローチです。OPが受け入れられた回答を変更できるかどうか疑問に思いますか?
チェイスフロレル2017年

1
ソースオブジェクトのプロパティを無視する同等のものはありますか?のようなものForAllOtherSourceMembers
SuperJMN 2019

44

AutoMapper 5.0以降、.TypeMaponプロパティ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>();
}

実装:

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

3
これを連鎖CreateMap<TSource,TDest>()式でどのように使用しProfileますか?
jmoerdyk

2
これをありがとう。GetUnmappedPropertyNamesメソッドは、ソースと宛先の両方で、マップされていないすべてのプロパティ名を返します。これは、リバースマップでは壊れているようです。そのため、マップされていないプロパティがソースまたは宛先にあるかどうかを確認して無視するために、IgnoreUnmappedに小さな変更を加える必要がありました。それに応じて。問題と更新を示すフィドルは次のとおり
Mun

1
回答を更新して、調査結果を含めました。ソースマッピングを使用していないため、これに遭遇していませんでした。ありがとう。
Richard

1
これは、利用可能なリフレクションがないとPCLでは機能しません。GetProperty(propName)は存在しません。
ジョージタスクス2016年

これがどのように問題の解決策であるのか、あるいはこれがどのようにして何をするのかわかりません。マップされていないプロパティは、マップされていないため、すでに無視さます。ポスターは、「明示的にマップされていない小道具をどのように無視するか」と述べました。つまり、Src.MyPropとDest.MyPropがある場合、MyPropのMapFrom&ForMemberへの明示的な呼び出しがない限り、そのマッピングは無視する必要があります。したがって、デフォルトのマッピングは無視する必要があります。このソリューションが行う唯一のことは、config-valid-assertを通過させることです。これは、マッピングを機能させるために必要ではありません。
N73k 2017年

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;
}

16

バージョン4.2.0以降で非静的APIを使用している場合は、次の拡張メソッド(ここでは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;
}

ここで重要なのは、静的APIが削除されると、などのコードMapper.FindTypeMapForが機能しなくなるため、expression.TypeMapフィールドが使用されることです。


7
5.0以降、expression.TypeMapは使用できなくなりました。これが5.0
Richard

public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)タイプの問題を修正するために使用する必要がありました。
Nick M

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());
    }
}

この場合、出力オブジェクトのIdフィールドのみが解決され、その他はすべてスキップされます。魅力のように機能します。トリッキーな拡張機能はもう必要ないようです。


10

AutoMapper 4.2に対するRobert Schroederの回答を更新しました。非静的マッパー構成では、を使用できません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の回答の
Michael Freidgeim

AM 4.2で動作します
Leszek P

8

特定のメンバーを無視することをどのように指定しますか?適用したい規約、基本クラス、または属性はありますか?すべてのマッピングを明示的に指定するビジネスに入ると、AutoMapperからどのような値が得られるかわかりません。


ジミー、あなたは露骨さについてポイントを持っています。これを最もエレガントな方法で達成する方法については、基本クラスと属性はこの状況では機能しません。ターゲットクラスは実際には私の制御下にないため、XSDデータコントラクトから自動生成されるので、各生成サイクル後にこのコードを手動で編集します。解決策は具体的なケースに依存すると思います。たぶん、ウィンザー城がコンテナに登録するコンポーネントを選択するために提供するものに似た流暢なインターフェイスが解決策になるでしょうか?
Igor Brejc 2009年

ああ、それは今より理にかなっています。これは興味深い機能です。2.1の時間枠で見ていきます。
ジミーボガード

2
存在しないすべてのフィールドを「無視」できる構成可能な値を持つだけではどうでしょうか。
Ricardo Sanchez

6
これは質問に対する答えではありません。
user2864740 2015年

こんにちはジミー、あなたは作家ですよね?デフォルトの動作であるすべての存在しないプロパティを無視できるようにしたいと思います(フラグによって制御される場合があります)。また、AutoMapperから奇妙なエラーが発生し、理解できません。それは私にどんな詳細も与えません。
直美

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

1

多くのメンバーを無視することに関する唯一の情報は、このスレッド-http://groups.google.com/group/automapper-users/browse_thread/thread/9928ce9f2ffa641fです。ProvidingCommonBaseClassConfigurationで使用されているトリックを使用して、同様のクラスの共通プロパティを無視できると思います。
また、「残りを無視する」機能に関する情報はありません。以前にコードを見たことがありますが、そのような機能を追加することは非常に困難です。また、いくつかの属性を使用して、無視されたプロパティでマークを付けて、マークされたすべてのプロパティを無視する汎用/共通コードを追加することもできます。


1
おそらく1つの方法は、ForAllMembersメソッドを使用して、無視してはならないプロパティのプロパティ名を含む文字列を受け取る独自のIMemberConfigurationExpressionを実装し、残りのプロパティを調べてIgnore()を呼び出すことです。ただのアイデアですが、うまくいくかどうかはわかりません。
Igor Brejc

はい、これも機能しますが、この方法は属性を使用するよりも注意が必要ですが、柔軟性が高くなります。銀の弾丸がないのは残念です
。– zihotki

1

私はこれが古い質問であることを知っていますが、あなたの質問に@jmoerdyk:

これをプロファイルのチェーンされたCreateMap()式でどのように使用しますか?

あなたはこのような答えをプロフィールの中で使うことができます

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

0

ForAllMembersを使用できますが、このように必要な場合のみ上書きできます

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

注意してください、それはすべてを無視します、そしてカスタムマッピングを追加しない場合、それらはすでに無視されており機能しません

また、AutoMapperの単体テストがある場合にも言いたいです。そして、すべてのプロパティが正しくマッピングされたすべてのモデルをテストします。そのような拡張メソッドは使用しないでください。

あなたは明示的に無視を書くべきです


-1

宛先タイプに存在しないプロパティを無視するための現在の(バージョン9)ソリューションは、反転マッピングを作成してそれを逆にすることです。

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

-2

バージョン3.3.1では、IgnoreAllPropertiesWithAnInaccessibleSetter()またはIgnoreAllSourcePropertiesWithAnInaccessibleSetter()メソッドを使用するだけです。


6
これは、元の投稿者の質問に従って機能しません。これらのメソッドは、保護されたプロパティまたはプライベートプロパティのみを無視し、ソースから欠落しているが宛先タイプに存在するプロパティは無視しません。
Dan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.