web-dev-qa-db-ja.com

AutoMapperを使用して、宛先オブジェクトをソースオブジェクトの子オブジェクトにマップする方法

次のようなソースオブジェクトと宛先オブジェクトがあります。

class ProductWithCategories // Source class
{
    public Product Product { get; set; } // Product is an EF entity class
    public IEnumerable<Category> Categories { get; set; }
}

class ProductViewModel // Dest class
{
    public int Id { get; set; }
    // Other properties with the same name as Product class

    public IEnumerable<CategoryViewModel> Categories { get; set; }
}

したがって、source.Productの値をdestにマップし、次にsource.Categoriesdest.Categoriesにマップする必要があります。 AutoMapperで可能ですか?

私はこれを試しました、そしてそれが失敗したとき私は驚きませんでした:

        config.CreateMap<ProductWithCategories, ProductViewModel>()
            .ForMember(q => q, option => option.MapFrom(q => q.Product))
            .ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories));

これが私が受け取った例外です:

[AutoMapperConfigurationException:メンバーのカスタム構成は、タイプの最上位の個々のメンバーに対してのみサポートされています。]

12
Luke Vo

OPとの議論の後、彼の主な必要性は、ソースオブジェクト内の子/ネストされたオブジェクトを、フラット化された宛先オブジェクトにすばやくマップすることであることがわかりました。彼は宛先のすべてのプロパティのマッピングを書きたくありません。

これを実現する方法を次に示します。

  • Productのメンバーをフラット化するために使用されるマッピングProduct-> ProductViewModelを定義します
  • CategoryからCategoryViewModelへのマッピングを定義する
  • カテゴリをマップするマッピングProductWithCategories-> ProductViewModelを定義し、アフターマップでProductをマップします。

    config.CreateMap<ProductWithCategories, ProductViewModel>() .ForMember(q => q.Id, option => option.Ignore()) // flattened in AfterMap .ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories)) .AfterMap((src, dst) => Mapper.Map(src.Product, dst));

27

AutoMapperの最新バージョンを使用すると、次のようなことができます。

config.CreateMap<Product, ProductViewModel>()
      .ForMember(q => q.Categories, option => option.Ignore());

config.CreateMap<ProductWithCategories, ProductViewModel>()
      .ConstructUsing(s => AutoMapper.Mapper.Map<ProductViewModel>(s.Product))
      .ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories))
      .ForAllOtherMembers(o => o.Ignore();

ConstructUsing()を使用して、ソースのネストされたchild [ren]から基本クラスを生成し、データを設定します。そのようなネストされた子が複数ある場合、最初のMap()呼び出しによって生成されたインスタンスに連続してそれぞれをマッピングするために、いくつかのマッピング呼び出しを行う必要があります。 .ForAllOtherMembers()は比較的最近のものです(それがない場合は、新しいバージョンのAutoMapperを入手してください)。残念ながら、マッピングが必要であるがマップの更新を忘れている宛先メンバーを追加する場合、構成の検証は行われないため、少し安全ではありません。捕まえろ。

13
Cliff Hudson

エラーを生成する問題の行は

.ForMember(q => q, option => option.MapFrom(q => q.Product))

エラーメッセージはわかりにくいですが、宛先プロパティを明示的に記述する必要があります。

.ForMember(q => q.Id, option => option.MapFrom(q => q.Product.Id))
.ForMember(q => q.OtherProperty, option => option.MapFrom(q => q.Product.OtherProperty))

また、CategoryからCategoryViewModelへのマッピングを定義する必要があります。

.ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories))

働く:

config.CreateMap<Category, CategoryViewModel>();
0

あなたはそうするべきです-

AutoMapper.Mapper.CreateMap<Category, CategoryViewModel>();
AutoMapper.Mapper.CreateMap<ProductWithCategories, ProductViewModel>()
     .ForMember(a => a.Id, b => b.ResolveUsing(c => c.Product != null ? c.Product.MyProperty : 0))
     .ForMember(a => a.Categories, b => b.ResolveUsing(c => c.Categories));

ただし、ProductViewModelIdのようなプロパティ)からこれらのプロパティを別のクラス内にラップすることをお勧めします。そして、オートマッパーのように動作するように別のマップを作成します。

0