88

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

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

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

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


次の目標を達成するために、このコードをどのように整理し、どこに配置すればよいでしょうか?

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

4 に答える 4

37

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

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

... (実装) RebuildViewModelは呼び出すだけですBuildViewModel(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 cuz を書きます。それははるかに短いですが、それはそれほど重要ではありません

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

于 2010-05-05T18:06:41.957 に答える
7

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によって行われ、データアクセスはリポジトリによって行われます。

于 2010-02-05T13:06:34.297 に答える
5

ビューモデルという用語は、クライアント インタラクションの両方向に再利用されていると言いたいです。実際の ASP.NET MVC コードを十分に読んだことがあれば、おそらく ViewModel と EditModel の違いを見たことがあるでしょう。それが重要だと思います。

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

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

一つのアイデア

つまり、Model -> ViewModel から移動する AutoMapper 構成と、EditModel -> Model から移動する別の AutoMapper 構成を非常に簡単に作成できます。次に、さまざまなコントローラー アクションで AutoMapper を使用する必要があります。地獄 EditModel には、モデルに対してそのプロパティを検証し、それらの値をモデル自体に適用する関数を含めることができます。他には何もしていません.MVCにModelBindersがあり、リクエストをEditModelにマップしています。

別のアイデア

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

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

デフォルトのものよりもインテリジェントなModelBinderが必要ですが、優れたJSONシリアライザーは、クライアント側のアクションオブジェクトからサーバー側のアクションオブジェクトへのマッピングを処理できるはずです。サーバー側のもの (2 層環境にいる場合) は、使用するモデルに対するアクションを完了するメソッドを簡単に持つことができます。そのため、Controller アクションは、Model インスタンスがプルする 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が必要であり、プロジェクトがどのように展開するかをまだ確認できていませんが、投稿はどのように行うかを考えようとしていました.行ってまた戻ってきたので、自分の考えを述べようと思いました。お役に立てば幸いです。インタラクションを管理する他の方法についてもぜひお聞かせください。

于 2010-10-08T13:42:33.360 に答える
0

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

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

于 2014-05-29T15:02:20.330 に答える