web-dev-qa-db-ja.com

POSTアクションでビューモデルをドメインモデルにマップし直す方法は?

ViewModelの使用とAutomapperの利用に関するインターネットの記事はすべて、「Controller-> View」方向マッピングのガイドラインを示しています。ドメインモデルとすべての選択リストを1つの特殊なViewModelに取り込み、ビューに渡します。それは明確で問題ありません。
ビューにはフォームがあり、最終的にPOSTアクションになります。ここで、すべてのモデルバインダーが)とともにシーンに来ます[明らかに]別の命名規則の少なくとも一部で、元のViewModelに[明らかに]関連するビューモデルバインディングと検証のため。

ドメインモデルにどのようにマッピングしますか?

挿入アクションとし、同じAutomapperを使用できます。しかし、更新アクションの場合はどうでしょうか?リポジトリからドメインエンティティを取得し、ViewModelの値に従ってプロパティを更新し、リポジトリに保存する必要があります。

補足1(2010年2月9日):モデルのプロパティを割り当てるだけでは不十分な場合があります。ビューモデルの値に応じて、ドメインモデルに対して何らかのアクションを実行する必要があります。つまり、ドメインモデルでいくつかのメソッドを呼び出す必要があります。おそらく、ビューモデルを処理するために、コントローラーとドメインの間にある種類のアプリケーションサービスレイヤーがあるはずです...


次の目標を達成するためにこのコードを整理する方法と配置する場所?

  • コントローラを薄く保つ
  • soCの実践を尊重する
  • ドメイン駆動設計の原則に従う
  • 乾く
  • つづく ...
85

IBuilderインターフェースを使用し、 ValueInjecter を使用して実装します

_public interface IBuilder<TEntity, TViewModel>
{
      TEntity BuildEntity(TViewModel viewModel);
      TViewModel BuildViewModel(TEntity entity);
      TViewModel RebuildViewModel(TViewModel viewModel); 
}
_

...(実装)RebuildViewModelBuildViewModel(BuilEntity(viewModel))を呼び出すだけです

_[HttpPost]
public ActionResult Update(ViewModel model)
{
   if(!ModelState.IsValid)
    {
       return View(builder.RebuildViewModel(model);
    }

   service.SaveOrUpdate(builder.BuildEntity(model));
   return RedirectToAction("Index");
}
_

ちなみにViewModelを書くのではなく、Inputを書くのはずっと短いのですが、それは本当に重要ではありません
それが役に立てば幸い

更新:現在、このアプローチを ProDinner ASP.net MVC Demo App で使用しています。IMapperと呼ばれています。また、このアプローチが詳細に説明されている場所で提供されるpdf

37
Omu

AutoMapperなどのツールを使用して、既存のオブジェクトをソースオブジェクトのデータで更新できます。更新のためのコントローラーアクションは次のようになります。

[HttpPost]
public ActionResult Update(MyViewModel viewModel)
{
    MyDataModel dataModel = this.DataRepository.GetMyData(viewModel.Id);
    Mapper<MyViewModel, MyDataModel>(viewModel, dataModel);
    this.Repostitory.SaveMyData(dataModel);
    return View(viewModel);
}

上記のスニペットに表示されるものとは別に:

  • モデルを表示するPOSTデータ+検証はModelBinderで行われます(カスタムバインディングで拡張できます)
  • エラー処理(つまり、リポジトリによるデータアクセス例外スローのキャッチ)は、[HandleError]フィルターによって実行できます。

コントローラーアクションは非常に薄く、懸念事項は分離されています。マッピングの問題はAutoMapper構成で対処され、検証はModelBinderによって行われ、データアクセスはリポジトリによって行われます。

7
PanJanek

クライアントの相互作用の両方向にViewModelという用語を再利用すると言いたいと思います。野生で十分なASP.NET MVCコードを読んでいれば、おそらくViewModelとEditModelの違いを見たことがあるでしょう。それは重要だと思います。

ViewModelは、ビューのレンダリングに必要なすべての情報を表します。これには、静的な非インタラクティブな場所でレンダリングされるデータや、純粋に何をレンダリングするかを決定するためのチェックを実行するためのデータも含まれます。通常、コントローラーのGETアクションは、そのビューのViewModelをパッケージ化する役割を果たします。

EditModel(またはおそらくActionModel)は、ユーザーがそのPOSTに対して実行したいアクションを実行するために必要なデータを表します。そのため、EditModelは実際にアクションを記述しようとしています。これはおそらくViewModelから一部のデータを除外しますが、関連しているものの、それらが実際に異なることを認識することが重要だと思います。

1つのアイデア

つまり、Model-> ViewModelから行くためのAutoMapper構成と、EditModel-> Modelから行くための別の構成を簡単に持つことができるということです。その後、異なるコントローラーアクションはAutoMapperを使用するだけです。地獄EditModelは、モデルに対してプロパティを検証し、それらの値をモデル自体に適用するための関数を持つことができます。それは他に何もしておらず、とにかくEditModelにリクエストをマッピングするMVCにModelBinderがあります。

別のアイデア

私が最近考えていたActionModelのアイデアからの一種の仕事は、クライアントがあなたにポストバックしているのは、実際にはユーザーが実行したいくつかのアクションの説明であり、1つの大きなデータの塊ではないということです。これは確かに管理するためにクライアント側でいくつかのJavascriptを必要としますが、アイデアは興味深いと思います。

基本的に、ユーザーが表示した画面でユーザーがアクションを実行すると、Javascriptはアクションオブジェクトのリストの作成を開始します。たとえば、ユーザーが従業員情報画面にいる可能性があります。従業員が最近結婚したため、姓を更新して新しい住所を追加します。カバーの下では、リストに対してChangeEmployeeNameおよびAddEmployeeMailingAddressオブジェクトが生成されます。ユーザーが「保存」をクリックして変更をコミットし、2つのオブジェクトのリストを送信します。各オブジェクトには、各アクションの実行に必要な情報のみが含まれています。

デフォルトよりもインテリジェントなModelBinderが必要ですが、優れたJSONシリアライザーは、クライアント側のアクションオブジェクトからサーバー側のオブジェクトへのマッピングを処理できる必要があります。サーバー側のサーバー(2層環境の場合)には、使用するモデルのアクションを完了するメソッドが簡単にあります。したがって、コントローラーアクションは、プルするモデルインスタンスのIDと、実行するアクションのリストを取得するだけです。または、アクションのIDを使用して、アクションを非常に分離します。

そのため、サーバー側で次のようなことが実現される可能性があります。

public interface IUserAction<TModel>
{
     long ModelId { get; set; }
     IEnumerable<string> Validate(TModel model);
     void Complete(TModel model);
}

[Transaction] //just assuming some sort of 2-tier with transactions handled by filter
public ActionResult Save(IEnumerable<IUserAction<Employee>> actions)
{
     var errors = new List<string>();
     foreach( var action in actions ) 
     {
         // relying on ORM's identity map to prevent multiple database hits
         var employee = _employeeRepository.Get(action.ModelId);
         errors.AddRange(action.Validate(employee));
     }

     // handle error cases possibly rendering view with them

     foreach( var action in editModel.UserActions )
     {
         var employee = _employeeRepository.Get(action.ModelId);
         action.Complete(employee);
         // against relying on ORMs ability to properly generate SQL and batch changes
         _employeeRepository.Update(employee);
     }

     // render the success view
}

ModelBinderを使用して正しいIUserActionインスタンスとIUserActionインスタンスを取得し、正しいロジック自体を実行するか、(おそらく)情報を使用してモデルを呼び出すため、ポストバックアクションは非常に汎用的になります。

3層環境の場合、IUserActionを単純なDTOにして、境界を越えて撮影し、アプリレイヤーで同様の方法で実行することができます。その層をどのように行うかに応じて、非常に簡単に分割し、トランザクションのままにすることができます(頭に浮かぶのは、Agathaの要求/応答であり、DIおよびNHibernateのIDマップを利用します)。

とにかく、それは完璧なアイデアではないと確信しています。管理するにはクライアント側のJSが必要です、そしてプロジェクトがまだどのように展開するかを見ることができませんでしたが、投稿はどのように考えるかを試みていましたそこに行き、また戻って、私は自分の考えを述べると思った。私はそれが助けになることを望み、相互作用を管理する他の方法を聞きたいです。

5
Sean Copenhaver

ビューモデルはドメインモデルよりも多く作成される可能性があるため、ビューモデルをドメインにマッピングする必要はありません。画面(ui)用に最適化された、ドメインモデルとは異なるビューモデル。

http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/

0
oguzh4n