web-dev-qa-db-ja.com

MVC / ASP.NETMVCでのモデルとコントローラーの正しい使用

GetProducts()というメソッドを持つServiceクラスがあります。これはビジネスロジックをカプセル化し、リポジトリを呼び出して製品のリストを取得します。

私のMVCビューは、その製品のリストをMVCSelectListとして表示したいと考えています。そのロジックを実行する正しい場所はどこですか。私には3つの選択肢があるようです:

  1. モデル

    モデルは、ProductSelectListというプロパティを公開する必要があります。このプロパティのゲッターがビューによって呼び出されると、モデルはService.GetProducts()を呼び出し、結果をSelectListに変換してから渡す必要があります。

    もっともらしい議論:モデルはビジネスロジックとリポジトリを呼び出す必要があります。ビューは、事前に定義されたデータをレンダリングするだけです。コンテキストデータをモデルに渡すことを除いて、コントローラーは関与しないでください。

  2. 表示

    ビューには、Service.GetProducts()を直接呼び出し、結果をSelectListインラインに変換するコードが含まれている必要があります。

    もっともらしい議論:ビューは、特にビューで使用するために、このデータを直接呼び出す必要があります。とにかく抽象化されたサービスメソッドを呼び出しているので、モデルやコントローラーを関与させる必要はありません。したがって、他のものは追加のオーバーヘッドを追加するだけです。

  3. コントローラー

    コントローラーはService.GetProducts()を呼び出し、結果をSelectListに変換して、モデルに渡す必要があります。モデルには、単純なProductSelectListプロパティが含まれている必要があります。ビューは、レンダリングのためにこのプロパティにアクセスします。

    もっともらしい引数:コントローラーはServiceメソッドに提供するパラメーターを知っているので、呼び出しを行う必要があります。モデルは、コントローラーによって入力されるデータの単純なプレースホルダーである必要があります。ビューの仕事は、モデルからのデータを単純にレンダリングすることです。

正解はモデルであるという感じがありますが、他の2つはいくつかの合理的なポイントを示しています。おそらく、モデルとは別のサービスクラスをすでに持っていることで、水を濁らせたのでしょうか。

誰かが自分の意見を共有したいと思いますか?これはただの好みの問題ですか?

29
James McCormack

私は個人的にNumber 3のロジックをサブスクライブし、controllerを許可しますモデルにデータを入力します(または、場合によっては区別されるようにモデルを表示します)。

  • 私の見解は馬鹿げていて、データしか表示していません。
  • ビューモデルにビューに必要な情報を保存させ、他のプロパティをより適切な形式にフォーマットする「getonly」プロパティを公開することがあります。モデルがサービスにアクセスする必要がある場合は、何か問題があると感じます。
  • コントローラーはすべての情報をまとめて収集します(ただし、実際の作業は行わず、サービスに残されます。

あなたの例では、コントローラーのアクションは次のようになります。

public ActionResult Index()
{
    IndexViewModel viewModel = new IndexViewModel();
    viewModel.ProductSelectList = new SelectList(Service.GetProducts(), "Value", "Name");
    return View(viewModel);
}

そして私のビューモデルは次のようになります:

public class IndexViewModel()
{
   public SelectList ProductSelectList { get; set; }
   public int ProductID { get; set; }
}

ビューの適切な部分は次のようになります。

@Html.DropDownListFor(x => x.ProductID, Model.ProductSelectList);

このように、私は何かに問題があり、すべてが非常に特定の場所にある場合にどこを見ればよいかを知っていることに満足しています。

しかし、これらのことでいつもそうであるように思われる正しい方法はありません。 Stephen Waltherは MVCのヒントに関する優れたブログシリーズ を持っています。ある人は、ビューモデルの強調について話し、入力したSelectListではありませんが、SelectListは、製品のリストとほぼ同じようにデータです。

14
Amadiere

従来のMVCアーキテクチャでは、Modelは、ビューデータのコンテナをはるかに超えるものであってはならないため、ViewModelと呼ばれることがよくあります。 ViewModelは、サービスレイヤーが管理するEntity Model(s)とは異なります。

次に、コントローラーは、サービスレイヤーによって返されたエンティティモデルからViewModelを設定する責任があります。

利便性のため、一部の開発者はサービスレイヤーエンティティをViewModelで直接使用しますが、長期的には頭痛の種になる可能性があります。これを回避する1つの方法は、 AutoMapper などのツールを使用して、ViewModelおよびエンティティモデルとの間のデータのシャッフルを自動化することです。

コントローラは次のようになります。エンティティモデルからビューモデルへのマッピングがあるため、SSNなどのデータはビューに公開されないことに注意してください。

public class Customer : IEntity
{
  public string CustomerID { get; set; }
  public string SSN { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }    
  public Address Address { get; set; }
}

public class CustomerEditViewModel
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string Address1 { get; set; }
  public string Address2 { get; set; }
  public string Country { get; set; }
  public string City { get; set; }
  public string State { get; set; }
  public string Zip { get; set; }
  public string PhoneNumber { get; set; }
}

public class CustomerController
{
  [AcceptVerbs (HttpVerbs.Get)]
  public ActionResult Edit ()
  {
    Customer customer = _customerService.GetCustomer (User.Identity.Name);

    var model = new CustomerEditViewModel ()
    {
      FirstName = customer.FirstName,
      LastName = customer.LastName,
      Address1 = customer.Address.Address1,
      Address2 = customer.Address.Address2,
      Country = customer.Address.Country,
      City = customer.Address.City,
      State = customer.Address.State,
      Zip = customer.Address.Zip,
      PhoneNumber = customer.Address.PhoneNumber,
    };

    return View (model);
  }
}
7
Todd Smith

これを処理する方法はいくつかあることは間違いありません。MVP、MVVMなどのバリエーションを検討する前でもそうです。特にASP.NetMVCについて質問しているので、Microsoftに任せます。

MVCモデルには、ビューまたはコントローラーに含まれていないすべてのアプリケーションロジックが含まれています。モデルには、アプリケーションのビジネスロジック、検証ロジック、およびデータベースアクセスロジックがすべて含まれている必要があります。たとえば、Microsoft Entity Frameworkを使用してデータベースにアクセスしている場合は、ModelsフォルダーにEntity Frameworkクラス(.edmxファイル)を作成します。

ビューには、ユーザーインターフェイスの生成に関連するロジックのみを含める必要があります。コントローラーには、正しいビューに戻るか、ユーザーを別のアクション(フロー制御)にリダイレクトするために必要な最小限のロジックのみを含める必要があります。他のすべてはモデルに含まれている必要があります。

一般的に、ファットモデルとスキニーコントローラーを目指して努力する必要があります。コントローラメソッドには、数行のコードのみを含める必要があります。コントローラのアクションが太くなりすぎる場合は、ロジックをModelsフォルダの新しいクラスに移動することを検討する必要があります。

出典

あなたの電話はモデルに属していると思います。

7
ajk

覚えておくべきことの1つは、SelectListクラスはMVCのみに固有であるということです。したがって、私の意見では、ビジネスロジックに含めるべきではなく、モデルクラスはそのカテゴリに分類されます。したがって、選択リストは代わりにビューモデルクラスの一部である必要があります。

これは私のプロジェクトで機能する方法です:

  • コントローラメソッドが呼び出されます
  • コントローラはリポジトリ(つまり、ビジネスロジック)を使用してモデルデータを取得します
  • コントローラは必要に応じてモデルデータを変換し、ビューモデルオブジェクトを作成します
  • コントローラはビ​​ューモデルをビューに渡します
  • ビューは、物事を表示または非表示にするための限られたロジックでビューモデルのデータを表示します。
3
Shea Daniels

オプション1を使用します。

モデルは、ビジネスロジックなどを呼び出す場所です。

ビュー-ViewModelにすでに入力されているもののみを表示する必要があります。

コントローラー-コントローラーの仕事は、(Web要求から)入ってくるトラフィックを、要求の処理を担当するロジックに転送することです。したがって、用語 'コントローラー'。

これらには常に例外がありますが、(構造的に)最適な場所はモデルです。

1
cyrotello

私がMVCを始めてこの解決策を思いついたとき、私はこの問題を抱えていました。

コントローラはサービスレイヤと通信します。サービスレイヤーには私のドメインモデルが含まれており、コントローラーからの要求に対するすべての処理を実行します。サービスレイヤーは、コントローラーからの要求を満たすためにViewModelsも返します。

サービス層はリポジトリを呼び出し、ViweModelsを構築するために必要なエンティティを取得します。私はよくオートマッパーを使用して、ビューモデルまたはビューモデル内のコレクションにデータを入力します。

したがって、私のビューモデルには、ビューに必要なものがすべて含まれており、コントローラーは要求を処理して適切なサービスハンドラーに転送するだけです。

ビューモデルにSelectListsなどのビュー固有のアイテムを表示しても問題は発生しません。

1
Jeff Reddy

オプション1とオプション3の間で引き裂かれました。プレゼンテーション層の作業だけでなく、プロシージャ呼び出しでビューを汚染しているということで、オプション2を完全に除外しました。

個人的にはモデルでそれを行い、ゲッターはサービスレイヤーを呼び出しますが、モデルのデータを完全に含まないことにより、モデルにはビューがページをレンダリングするために必要な情報のみを含める必要があるという信念にも同意します。あなたがそれをビューに渡すとき、あなたはこれを壊しています。

ただし、ここでのもう1つのオプションは、サービス呼び出しを介して製品のDictionaryをビューに配置し、ビューを使用してDictionarySelectListに変換することにより、ビューとモデルの緊密な結合を回避することですが、これにより、情報も。

これは、論理を持って満足している場所に関する好みに要約されると思います。

1
Andy Hey

上記のどれでもない。

私のWebレイヤーには、基本的にhtmlビューとjavascriptビューがあります。モデルがビューに漏れてはならず、サービスも漏れてはなりません。

また、サービスとモデルをビューにバインドするインフラストラクチャレイヤーもあります。このレイヤーには、画面に表示されるものを表すクラスであるViewModels、サービス/モデルからデータを取得してビューモデルにマッピングする作業を行うMappers、および保存などのタスクを実行するTasksがあります。データの更新と削除。

Todd Smithが上記で示した例と同様に、このインフラストラクチャの多くをコントローラーに配置することは可能ですが、些細なビュー以外では、コントローラーにデータをロードしてビューモデルにデータを入力するためのコードが散らばっています。私は、ビューモデルごとに専用の単一責任マッパークラスを好みます。すると私のコントローラーは次のようになります

public class CustomerController
{
  [AcceptVerbs (HttpVerbs.Get)]
  public ActionResult Edit (int id)
  {
    return View (CustomerEditMapper.Map(id));
  }

  [AcceptVerbs (HttpVerbs.Post)]
  public ActionResult Save(CustomerEditViewModel model)
  {
    var errors = CustomerEditUpdatorCommand.Execute(model);
    ModelState.AddErrors(errors);
    return View ();
  }

}
1
Craig

オプション3を使用します。通常、MVCアプリは、コントローラーがサービスを呼び出してモデル(またはモデルのコレクション)を返し、ビューに渡されるように作成します。

私は通常、モデルを非常に薄く保ちます。これらは、検証属性を持つデータのフラット化された表現であり、それだけです。私はサービス(またはモデルビルダー)レイヤーを使用してモデルを構築し、モデルに対してビジネスロジックを実行します。それをモデルに埋め込む人もいますが、それは厄介なプロジェクトになります。

ビューがサービスを呼び出すことは絶対に望まないでしょう。

更新...
このSelectListあなたのモデルだと思います。代わりにそれがモデルの一部である場合、あなたは正しいです、あなたはそれをあなたのモデルに入れるべきです。しかし、私は一般的にそれをメソッド呼び出しにするのは好きではありません。モデルにプロパティがあります:

public SelectList Products { get; set; }

そして、私のサービスまたはモデルビルダークラスに実際にデータを設定してもらいます。私は通常、モデルにデータ指向のメソッドを持っていません。

1
Josh Anderson