web-dev-qa-db-ja.com

AutoMapperを介してドメインモデルをビューモデルにマッピングするかどうか

ドメインモデルの表示にビューモデルを使用したい。プロパティを表示用にカスタマイズしたいのですが、どうすればよいですか?また、表示にAutoMapperを使用することは良い習慣ですか?

以下はコードサンプルです。

public class BookController : BaseController
    {
        private IBookService bookService;

        public BookController(IBookService bookService)
        {
            this.bookService = bookService;
        }

        public ActionResult Details(int id)
        {
            var book = bookService.GetBookById(id);

            return View(Mapper.Map<BookView>(book));
        }
}

    public class Book 
    {        
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
    }

    public class BookView
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

別の方法を使用する場合、以下のように任意のプロパティをカスタマイズできます。

  public ActionResult Details(int id)
        {
            var book = bookService.GetBookById(id);

            return View(new BookView(book));
        }

    public class BookView
    {
       public BookView(Book book){
         Name = book.Name +" Decorated";
       }
        public int Id { get; set; }
        public string Name { get; set; }
    }

どうすればよいですか?また、表示にAutoMapperを使用することは良い習慣ですか?

更新

以下のシナリオではオートマッパーを使用する方が適切と思われます。たとえば、以下のようにビューモデルをドメインモデルにマッピングします。意見はありますか?

  [HttpPost]
    public ActionResult Create(BookView bookView)
    {
        try
        {
            var book = Mapper.Map<Book>(bookView);  //this is wrong

            bookService.SaveOrUpdate(book);

            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }

更新2

ビューモデルを介した複雑なカスタムディスプレイの場合、オートマッパーがマッピングできると想定して、オートマッパーを使用して表示ロジックをマッピングしたくありません。さまざまな目的が混在しているためです。例えば:

Mapper.CreateMap<Book, BookView>()
    .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name + " this is for display purpose"));

ただし、以下のような手動マッピングの使用は直感的に思えます。

 public BookView(Book book){
    //mapping here
}

更新3

Jimmy Bogard からの引用:

「=」演算子を使用したくないのでAutoMapperを使用するのは少し面倒です。代わりに、それを使用して平坦化および再形成し、宛先タイプの環境に最適化します。 AutoMapperに対する私の元々の動機は次のとおりでした。

DTOにマッピングすることにより、ドメインレイヤーを他のレイヤーから保護できます。

リンクをありがとう@AndrewWhitaker

17
Pingpong

これはAutoMapperの良い使用例です(私はこの方法を多くのプロジェクトでこの方法で広範囲に使用して成功しています)。通常、ドメインエンティティをビューに公開したいnot(MVCでは、これはモデルをビューに直接公開することになりますが、これは正しくありません)。

ドメインエンティティとビューモデル間の1-1マッピングは必要ありません。それらを完全に異なるものにして、CreateMap<>呼び出しでマッピングをカスタマイズできます。あなたの例を使用するには:

Mapper.CreateMap<Book, BookView>()
    .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name + " Decorated"));

最悪の場合、複雑なケースではオートマッパーを破棄するか、オートマッパーでカスタムタイプリゾルバーを使用してジョブを完了できます。

実際これがJimmy Bogard(作者)がAutoMapperの使用を推奨する方法です 。特に、厳密に型指定されたビューで使用するためのドメインエンティティからASP.NET MVC ViewModelへのマッピングについて言及しています。

別の利点は、マッピングプロファイルを単体テストできることです。このようにして、ViewModelとModelの不一致が生じた場合、ユニットテストが失敗します。

更新:

質問に追加した引用は、AutoMapperを使用してドメインモデルからViewModelにマッピングすることをさらにサポートしていると思います。

代わりに、それを使用して平坦化および再形成し、宛先タイプの環境に最適化します。

したがって、私の例では、あなたは間違いなく宛先タイプの環境に最適化する(この場合はビュー)になります。

また、上記で参照したリンクごとにnotオートマッパーを使用してtoドメインをマッピングし、fromのみを使用する必要があります。このことを念頭に置いて、ビューから受け取ったものからドメインエンティティを作成/更新するためのロジックを記述する必要があります。コントローラーアクションはドメインエンティティを直接取得するべきではないことに注意してください(ビューから直接取得されるデータを信頼しないでください。モデルにドメインエンティティが有効かどうかを判断させます)。

18
Andrew Whitaker