MVCのViewModelとは何ですか?


429

ASP.NET MVCは初めてです。ViewModelの目的の理解に問題があります。

ViewModelとは何ですか?なぜASP.NET MVCアプリケーションにViewModelが必要なのですか?

私がその働きと説明についての良い例を手に入れたら、それはより良いでしょう。


4
この投稿はあなたが探しているものです-「ASP.NET MVC ViewModelとは」
ユスボフ

6
この記事は偉大なりますrachelappel.com/...
アンドリュー

回答:


607

A view modelは、静的テキストまたはデータベースに追加(または編集)できる入力値(テキストボックスやドロップダウンリストなど)に使用されるかどうかに関係なく、ビュー/ページに表示するデータを表します。それはあなたとは違うものdomain modelです。ビューのモデルです。

私たちはあなたが持っているとしましょうEmployee、あなたの従業員のドメインモデルを表すクラスをし、それは、次のプロパティ(一意の識別子、最初の名前、最後に作成された名前と日付)が含まれています。

public class Employee : IEntity
{
     public int Id { get; set; }

     public string FirstName { get; set; }

     public string LastName { get; set; }

     public DateTime DateCreated { get; set; }
}

ビューモデルは、ビューで使用するデータ(プロパティで表される)のみを含むという点で、ドメインモデルとは異なります。たとえば、新しい従業員レコードを追加するとします。ビューモデルは次のようになります。

public class CreateEmployeeViewModel
{
     public string FirstName { get; set; }

     public string LastName { get; set; }
}

ご覧のとおり、2つのプロパティのみが含まれています。これら2つのプロパティは、従業員ドメインモデルにもあります。これはなぜあなたが尋ねるかもしれませんか? Idビューからは設定されない場合があり、従業員テーブルによって自動生成される場合があります。またDateCreated、ストアドプロシージャまたはアプリケーションのサービスレイヤーで設定される場合もあります。だから、IdおよびDateCreatedビューモデルで必要とされていません。従業員の詳細(既にキャプチャされている従業員)を静的テキストとして表示するときに、これら2つのプロパティを表示することができます。

ビュー/ページをロードすると、従業員コントローラーのcreateアクションメソッドがこのビューモデルのインスタンスを作成し、必要に応じてフィールドに入力してから、このビューモデルをビュー/ページに渡します。

public class EmployeeController : Controller
{
     private readonly IEmployeeService employeeService;

     public EmployeeController(IEmployeeService employeeService)
     {
          this.employeeService = employeeService;
     }

     public ActionResult Create()
     {
          CreateEmployeeViewModel model = new CreateEmployeeViewModel();

          return View(model);
     }

     public ActionResult Create(CreateEmployeeViewModel model)
     {
          // Do what ever needs to be done before adding the employee to the database
     }
}

ビュー/ページは次のようになります(使用しているASP.NET MVCと仮定して、Razorビューエンジン):

@model MyProject.Web.ViewModels.CreateEmployeeViewModel

<table>
     <tr>
          <td><b>First Name:</b></td>
          <td>@Html.TextBoxFor(m => m.FirstName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.FirstName)
          </td>
     </tr>
     <tr>
          <td><b>Last Name:</b></td>
          <td>@Html.TextBoxFor(m => m.LastName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.LastName)
          </td>
     </tr>
</table>

したがって、検証はFirstNameおよびでのみ行われLastNameます。FluentValidationを使用すると、次のような検証が行われる可能性があります。

public class CreateEmployeeViewModelValidator : AbstractValidator<CreateEmployeeViewModel>
{
     public CreateEmployeeViewModelValidator()
     {
          RuleFor(m => m.FirstName)
               .NotEmpty()
               .WithMessage("First name required")
               .Length(1, 50)
               .WithMessage("First name must not be greater than 50 characters");

          RuleFor(m => m.LastName)
               .NotEmpty()
               .WithMessage("Last name required")
               .Length(1, 50)
               .WithMessage("Last name must not be greater than 50 characters");
     }
}

データアノテーションを使用すると、次のようになります。

public class CreateEmployeeViewModel : ViewModelBase
{
    [Display(Name = "First Name")]
    [Required(ErrorMessage = "First name required")]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    [Required(ErrorMessage = "Last name required")]
    public string LastName { get; set; }
}

覚えておくべき重要なことは、ビューモデルは、使用したいデータのみを表し、それ以外は何も表さないということです。30個のプロパティを持つドメインモデルがあり、単一の値のみを更新したい場合は、すべての不要なコードと検証を想像できます。このシナリオでは、ドメインモデルにあるすべてのプロパティではなく、ビューモデルにこの1つの値/プロパティのみが含まれます。

ビューモデルには、1つのデータベーステーブルのデータだけが含まれるとは限りません。別のテーブルのデータを組み合わせることができます。上記の私の従業員レコードの追加に関する例を見てみましょう。姓名だけでなく、従業員の部署を追加することもできます。この部門のリストは、Departmentsテーブルから取得されます。これで、1つのビューモデルにEmployeesDepartmentsテーブルからのデータがあります。次に、次の2つのプロパティをビューモデルに追加して、データを入力する必要があります。

public int DepartmentId { get; set; }

public IEnumerable<Department> Departments { get; set; }

従業員データ(既にデータベースに追加されている従業員)を編集する場合、上記の例と大差ありません。ビューモデルを作成し、たとえばそれを呼び出しますEditEmployeeViewModel。このビューモデルで編集したいデータ(姓、名など)のみを含めます。データを編集し、送信ボタンをクリックします。たとえばIdId値はおそらくURLに含まれるため、フィールドについてあまり心配する必要はありません。

http://www.yourwebsite.com/Employee/Edit/3

これIdを取得し、姓名の値とともにリポジトリレイヤーに渡します。

レコードを削除するとき、私は通常、編集ビューモデルと同じパスに従います。たとえば、次のようなURLもあります。

http://www.yourwebsite.com/Employee/Delete/3

ビューが初めて読み込まれるとき、Id3を使用してデータベースから従業員のデータを取得します。次に、ユーザーがどの従業員が削除されているかを確認できるように、ビュー/ページに静的テキストを表示します。ユーザーが[削除]ボタンをクリックすると、Id値3を使用してリポジトリレイヤーに渡します。Idテーブルからレコードを削除する必要があるだけです。

別のポイントとして、実際にはすべてのアクションにビューモデルは必要ありません。単純なデータであれば、のみを使用しても問題ありませんEmployeeViewModel。複雑なビュー/ページであり、それらが互いに異なる場合は、それぞれに個別のビューモデルを使用することをお勧めします。

これにより、ビューモデルとドメインモデルに関する混乱が解消されることを願っています。


5
@Kennyは:そして、それを表示:)私は言おうとしたことは、あなたが50のプロパティとビューだけで、それはちょうど5を表示するために、すべての50個のプロパティを送信するには使用されません5を表示する必要があるとドメインモデルを持って言うことができます
ブレンダンをVogt

5
@BrendanVogt –あなたはそれを説明する良い仕事をしましたが、私は「50の物件すべてを送る」ことのコストが何であるかを理解していません。その他のコードはすでに、すべての50個のプロパティで、モデルオブジェクトを作成し、それだけに別のクラスを維持するために価値があるようではありませんしていない 45個の特性を送信する-あなたがいる場合は特にかもしれない将来的にこれらの45体の特性のいずれかを送信します。
ケニーエビット2013

5
@BrendanVogt – LukLedの回答は、これらがなぜ有用であるかを理解するのに役立つと思います。特に、ViewModelは、「...異なるデータベースエンティティからの値を組み合わせる」[このフレーズが真実であると仮定している場合、 「データベースエンティティ」を「モデルオブジェクト」に置き換えます]。しかし、それでも、ViewModelsが対処することを意図した特定の問題は何ですか?リンクはありますか?自分では何も見つかりませんでした。[そして、私があなたを
つまんで

1
ViewModelsが複数のコレクション(またはクロスモデルプロパティ)をviewBagに詰め込むことなく単一のビューに送信するための良い方法であると誰かが言うのを聞いたばかりです。私には理にかなっています。
Ayyash、

3
批判的になって申し訳ありませんが、この回答は残念ながら不完全です。ページに表示する必要があるものだけとしてビューモデルを定義することは、「車とは何ですか?」「飛行機じゃない」という返事をもらいました。まあそれは本当ですが、あまり役に立ちません。VMのより正確な定義は、「ページをレンダリングするために必要なすべてのもの」です。一番下まで読んだら、VMを正しく簡単に構築するために必要なコンポーネントを特定しました。多くの場合、既存のドメインモデルとプレゼンテーションモデルを活用しています。
サム

133

ビューモデルは、特定のビューで使用されるデータモデルを表すクラスです。このクラスをログインページのモデルとして使用できます。

public class LoginPageVM
{
    [Required(ErrorMessage = "Are you really trying to login without entering username?")]
    [DisplayName("Username/e-mail")]
    public string UserName { get; set; }
    [Required(ErrorMessage = "Please enter password:)")]
    [DisplayName("Password")]
    public string Password { get; set; }
    [DisplayName("Stay logged in when browser is closed")]
    public bool RememberMe { get; set; }
}

このビューモデルを使用して、ビュー(Razorビューエンジン)を定義できます。

@model CamelTrap.Models.ViewModels.LoginPageVM

@using (Html.BeginForm()) {
    @Html.EditorFor(m => m);
    <input type="submit" value="Save" class="submit" />
}

そしてアクション:

[HttpGet]
public ActionResult LoginPage()
{
    return View();
}

[HttpPost]
public ActionResult LoginPage(LoginPageVM model)
{
    ...code to login user to application...
    return View(model);
}

これにより、この結果が生成されます(フォームは送信後、検証メッセージとともに表示されます)。

ご覧のとおり、ビューモデルには多くの役割があります。

  • ビューモデルは、ビューに表示されるフィールドのみで構成されるビューを文書化します。
  • ビューモデルには、データ注釈またはIDataErrorInfoを使用した特定の検証ルールが含まれる場合があります。
  • ビューモデルは、ビューが(のためにどのように見えるべきかを定義しLabelForEditorForDisplayForヘルパー)。
  • ビューモデルは、異なるデータベースエンティティの値を組み合わせることができます。
  • ビューモデルの表示テンプレートを簡単に指定し、DisplayForまたはEditorForヘルパーを使用して多くの場所で再利用できます。

ビューモデルとその取得の別の例:基本的なユーザーデータ、ユーザーの権限、ユーザー名を表示したいとします。必要なフィールドのみを含む特別なビューモデルを作成します。データベースのさまざまなエンティティからデータを取得しますが、ビューはビューモデルクラスのみを認識します。

public class UserVM {
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsAdministrator { get; set; }
    public string MothersName { get; set; }
}

検索:

var user = db.userRepository.GetUser(id);

var model = new UserVM() {
   ID = user.ID,
   FirstName = user.FirstName,
   LastName = user.LastName,
   IsAdministrator = user.Proviledges.IsAdministrator,
   MothersName = user.Mother.FirstName + " " + user.Mother.LastName
} 

user.Mother.FirstName + "" + user.Mother.LastNameは、ビューモデルの終了で実行する必要があります。すべてのロジックは、ビューモデル側で実行する必要があります。
Kurkula

3
@チャンダナ:ビューモデルで単純な連結を実行できると思います。2つのフィールドを一緒に表示することを意図している場合は、それらを公開する理由はありません。
LukLed 2015年

82

編集:私は私のブログでこの回答を更新しました:

http://www.samwheat.com/post/The-function-of-ViewModels-in-MVC-web-development

私の答えは少し長くなりますが、ビューモデルを他の一般的に使用されるモデルと比較して、なぜ違うのか、なぜ必要なのかを理解することが重要だと思います。

まとめると、尋ねられた質問に直接回答するには:

一般的に、ビューモデルは、ビューのレンダリングに必要なすべてのプロパティとメソッドを含むオブジェクトです。ビューモデルのプロパティは、多くの場合、顧客や注文などのデータオブジェクトに関連し、さらに、ユーザー名、アプリケーション名など、ページまたはアプリケーション自体に関連するプロパティも含みます。ビューモデルは、レンダリングエンジンに渡すのに便利なオブジェクトを提供します。 HTMLページを作成します。ビューモデルを使用する多くの理由の1つは、ビューモデルが、ユーザー入力の処理、データの検証、表示用のデータの取得などの特定のプレゼンテーションタスクを単体テストする方法を提供することです。

エンティティモデル(a.ka. DTOのa.ka.モデル)、プレゼンテーションモデル、およびビューモデルの比較を以下に示します。

データ転送オブジェクト、別名「モデル」

データ転送オブジェクト(DTO)は、データベースのテーブルスキーマに一致するプロパティを持つクラスです。DTOは、データストアとの間でデータを往復する一般的な使用法にちなんで名付けられました。
DTOの特性:

•ビジネスオブジェクト–その定義はアプリケーションデータに依存します。

•通常はプロパティのみが含まれ、コードは含まれません。

•主にデータベースとの間でデータを転送するために使用されます。

•プロパティは、データストア内の特定のテーブルのフィールドと完全またはほぼ一致します。

通常、データベーステーブルは正規化されているため、DTOも通常は正規化されています。これにより、データを表示するための使用が制限されます。ただし、特定の単純なデータ構造では、多くの場合非常にうまく機能します。

DTOがどのように見えるかの2つの例を次に示します。

public class Customer
{
    public int ID { get; set; }
    public string CustomerName { get; set; }
}


public class Order
{
    public int ID { get; set; }
    public int CustomerID { get; set; }
    public DateTime OrderDate { get; set; }
    public Decimal OrderAmount { get; set; }
}

プレゼンテーションモデル

プレゼンテーションモデルは、画面またはレポートにデータをレンダリングするために使用されるユーティリティクラスです。プレゼンテーションモデルは通常、複数のDTOのデータから構成される複雑なデータ構造をモデル化するために使用されます。プレゼンテーションモデルは、多くの場合、データの非正規化されたビューを表します。

プレゼンテーションモデルの特徴:

•ビジネスオブジェクト–その定義はアプリケーションデータに依存します。

•ほとんどのプロパティが含まれています。コードは通常、データのフォーマット、またはDTOとの間の変換に限定されます。プレゼンテーションモデルにはビジネスロジックを含めないでください。

•多くの場合、データの非正規化されたビューが表示されます。つまり、複数のDTOのプロパティを組み合わせることがよくあります。

•多くの場合、DTOとは異なる基本タイプのプロパティが含まれています。たとえば、ドルの金額は文字列として表されるため、カンマと通貨記号を含めることができます。

•多くの場合、それらの使用方法とオブジェクトの特性によって定義されます。つまり、グリッドをレンダリングするためのバッキングモデルとして使用される単純なDTOは、実際にはそのグリッドのコンテキストでのプレゼンテーションモデルでもあります。

プレゼンテーションモデルは、「必要に応じて」および「必要に応じて」使用されます(DTOは通常、データベーススキーマに関連付けられています)。プレゼンテーションモデルは、ページ全体、ページ上のグリッド、またはページ上のグリッドのドロップダウンのデータをモデル化するために使用できます。多くの場合、プレゼンテーションモデルには、他のプレゼンテーションモデルであるプロパティが含まれています。プレゼンテーションモデルは、特定のグリッドを単一のページにレンダリングするなど、使い捨ての目的で作成されることがよくあります。

プレゼンテーションモデルの例:

public class PresentationOrder
{
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
    public string CustomerName { get; set; }
    public Decimal OrderAmount { get; set; }
    public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}

モデルを見る

ビューモデルは、ビューをレンダリングするためのバッキングクラスであるという点でプレゼンテーションモデルに似ています。ただし、プレゼンテーションモデルやDTOとは、その構成方法が大きく異なります。ビューモデルには、プレゼンテーションモデルやDTOと同じプロパティが含まれていることがよくあります。このため、ビューモデルは互いに混同されることがよくあります。

ビューモデルの特性:

•ページまたは画面のレンダリングに使用される単一のデータソースです。通常、これは、ビューモデルが、ページ上の任意のコントロールがそれ自体を正しくレンダリングするために必要とするすべてのプロパティを公開することを意味します。ビューモデルをビューの単一のデータソースにすると、ユニットテストの機能と価値が大幅に向上します。

• アプリケーションデータで構成されるプロパティと、アプリケーションコードで使用されるプロパティを含む複合オブジェクトです。この特性は、再利用可能なビューモデルを設計する際に重要であり、以下の例で説明します。

•アプリケーションコードが含まれています。通常、ビューモデルには、レンダリング中、およびユーザーがページを操作しているときに呼び出されるメソッドが含まれています。このコードは通常、イベント処理、アニメーション、コントロールの可視性、スタイリングなどに関連しています。

•データの取得またはデータベースサーバーへの送信を目的としてビジネスサービスを呼び出すコードが含まれています。このコードは、誤ってコントローラーに配置されることがよくあります。コントローラからビジネスサービスを呼び出すと、通常、ユニットテストのビューモデルの有用性が制限されます。明確にするために、ビューモデル自体にはビジネスロジックを含めないでください。ビジネスロジックを含むサービスを呼び出す必要があります。

•多くの場合、他のページまたは画面の他のビューモデルであるプロパティが含まれています。

•「ページごと」または「画面ごと」と書かれている。固有のビューモデルは、通常、アプリケーションのすべてのページまたは画面に対して作成されます。

•ほとんどのページと画面は共通のプロパティを共有するため、通常は基本クラスから派生します。

モデル構成を表示

前述のように、ビューモデルは、アプリケーションプロパティとビジネスデータプロパティを1つのオブジェクトに結合するという点で、複合オブジェクトです。ビューモデルで使用される一般的に使用されるアプリケーションプロパティの例は次のとおりです。

•エラーメッセージ、ユーザー名、ステータスなど、アプリケーションの状態を表示するために使用されるプロパティ

•コントロールのフォーマット、表示、スタイル設定、またはアニメーション化に使用されるプロパティ。

•リストオブジェクトやユーザーが入力した中間データを保持するプロパティなど、データバインディングに使用されるプロパティ。

次の例は、ビューモデルの複合的な性質が重要である理由と、効率的で再利用可能なビューモデルを最適に構築する方法を示しています。

Webアプリケーションを作成していると仮定します。アプリケーション設計の要件の1つは、ページタイトル、ユーザー名、およびアプリケーション名をすべてのページに表示する必要があることです。プレゼンテーション注文オブジェクトを表示するページを作成する場合は、プレゼンテーションモデルを次のように変更します。

public class PresentationOrder
{
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
    public string CustomerName { get; set; }
    public Decimal OrderAmount { get; set; }
    public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}

このデザインはうまくいくかもしれませんが、注文のリストを表示するページを作成したい場合はどうでしょうか?PageTitle、UserName、およびApplicationNameの各プロパティは繰り返され、扱いにくくなります。また、クラスのコンストラクターでページレベルのロジックを定義したい場合はどうなりますか?表示されるすべての注文のインスタンスを作成すると、それを行うことができなくなります。

継承より構成

以下は、本当のビューモデルになるように注文プレゼンテーションモデルをリファクタリングする方法であり、単一のPresentationOrderオブジェクトまたはPresentationOrderオブジェクトのコレクションを表示するのに役立ちます。

public class PresentationOrderVM
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business properties
    public PresentationOrder Order { get; set; }
}


public class PresentationOrderVM
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

上記の2つのクラスを見ると、ビューモデルを考える1つの方法は、プロパティとして別のプレゼンテーションモデルを含むプレゼンテーションモデルであることがわかります。最上位のプレゼンテーションモデル(ビューモデル)には、ページまたはアプリケーションに関連するプロパティが含まれ、プレゼンテーションモデル(プロパティ)には、アプリケーションデータに関連するプロパティが含まれます。

デザインを一歩進めて、PresentationOrdersだけでなく、他のクラスにも使用できるベースビューモデルクラスを作成できます。

public class BaseViewModel
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
}

これで、PresentationOrderVMを次のように簡略化できます。

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public PresentationOrder Order { get; set; }
}

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

BaseViewModelをジェネリックにすることで、さらに再利用可能にすることができます。

public class BaseViewModel<T>
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business property
    public T BusinessObject { get; set; }
}

今、私たちの実装は簡単です:

public class PresentationOrderVM : BaseViewModel<PresentationOrder>
{
    // done!
}

public class PresentationOrderVM : BaseViewModel<List<PresentationOrder>>
{
    // done!
}

2
サムありがとう!! これは、ビューモデルである多面的なエンティティを完全に把握するのに役立ちました。私はMVCアーキテクチャを学んでいる大学生です。これにより、開発者に公開されている多くの機能が明らかになりました。できればあなたの答えの隣に星を付けます。
Chef_Code 2016年

1
@Sam 「ビューモデルには、プレゼンテーションモデルやDTOと同じプロパティが含まれていることが多く、このため、これらは互いに混同されることがよくあります。」それは、それらがプレゼンテーションモデルの代わりに一般的に使用されているということですか、それともプレゼンテーションモデル/ dtoを含むことを意図しているのですか?
Alexander Derck 2017

2
@AlexanderDerckそれらはさまざまな目的で使用されます。彼らは(誤って)一方を他方と混同しています。いいえ、通常はビューモデルの代わりにプレモデルを使用しません。はるかに一般的には、VMは、プレゼンテーションモデル、すなわち「が含まれている」ということです MyViewModel<MyPresModel>
サム・

2
@Samモデルオブジェクトがライブオブジェクト(nhibernateモデルなど)であると仮定すると、BusinessObjectを持つことで、モデル/ライブオブジェクトをビューに直接公開しませんか?つまり、ビジネスオブジェクトを使用して、データベースの状態を直接変更できますか?また、ネストされたビューモデルについてはどうですか?それには、複数のビジネスオブジェクトプロパティが必要です。
ムハンマドアリ

22

ビューに固有で、DB / Service / Dataストアに関連しないプロパティがある場合は、ViewModelsを使用することをお勧めします。たとえば、DBフィールド(または2つ)に基づいてチェックボックスを選択したままにしておきたいが、DBフィールド自体はブール値ではないとします。モデル自体でこれらのプロパティを作成し、データへのバインドから非表示にしておくことは可能ですが、そのようなフィールドとトランザクションの量によっては、モデルを乱雑にしたくない場合があります。

ビュー固有のデータや変換が少なすぎる場合は、モデル自体を使用できます


19

すべての投稿を読んだわけではありませんが、すべての回答に「理解する」のに役立つ概念が欠けているようです...

モデルがデータベースのTableに似ている場合、ViewModelはデータベースのViewに似ています-ビューは通常、1つのテーブルから少量のデータを返すか、複数のテーブル(結合)から複雑なデータセットを返します。

ViewModelsを使用して情報をビュー/フォームに渡し、フォームがコントローラーにポストバックするときにそのデータを有効なモデルに転送することに気づきました。これは、Lists(IEnumerable)の格納にも非常に便利です。


11

MVCにはビューモデルがありません。モデル、ビュー、コントローラーがあります。viewmodelはMVVM(Model-View-Viewmodel)の一部です。MVVMはプレゼンテーションモデルから派生し、WPFで普及しています。MVVMにもモデルがあるはずですが、ほとんどの人はそのパターンの要点を完全に逃し、ビューとビューモデルしかありません。MVCのモデルは、MVVMのモデルに似ています。

MVCでは、プロセスは3つの異なる責任に分けられます。

  • ビューは、ユーザーにデータを提示する責任があります
  • コントローラはページフローを担当します
  • モデルはビジネスロジックを担当します

MVCはWebアプリケーションにはあまり適していません。これは、Smalltalkがデスクトップアプリケーションを作成するために導入したパターンです。Web環境の動作は完全に異なります。40年前のコンセプトをデスクトップ開発からコピーしてWeb環境に貼り付けるのはあまり意味がありません。ただし、多くの人は、アプリケーションがコンパイルして正しい値を返すため、これは問題ないと考えています。つまり、私の意見では、特定のデザインの選択をOKと宣言するには不十分です。

Webアプリケーションのモデルの例は次のとおりです。

public class LoginModel
{
    private readonly AuthenticationService authentication;

    public LoginModel(AuthenticationService authentication)
    {
        this.authentication = authentication;
    }

    public bool Login()
    {
        return authentication.Login(Username, Password);
    }

    public string Username { get; set; }
    public string Password { get; set; }
}

コントローラは次のように使用できます:

public class LoginController
{
    [HttpPost]
    public ActionResult Login(LoginModel model)
    {
        bool success = model.Login();

        if (success)
        {
            return new RedirectResult("/dashboard");
        }
        else
        {
            TempData["message"] = "Invalid username and/or password";
            return new RedirectResult("/login");
        }
    }
}

コントローラーのメソッドとモデルは小さく、簡単にテストでき、要点がわかります。


MVVMアーキテクチャについての洞察をありがとう、しかしMVCはなぜ問題があるのですか?あなたの推論は疑わしく、愛好の疑いがあります。私がMVVMについて何も知らないのは確かですが、MVCなどのアーキテクチャが5万行のコードを記述しなくても動作を模倣できる場合、何が問題なのでしょうか。
Chef_Code

@Chef_Code:それは疑わしいまたは好意的ではありません:MVCについての元の論文を読んでください。ソースに戻ることは、疑いなく群れを盲目的に追跡するよりもはるかに優れています(「ベストプラクティス」とも呼ばれます)。MVCは、はるかに小さいユニットを対象としています。たとえば、画面上のボタンは、モデル、ビュー、およびコントローラーで構成されています。Web-MVCでは、ページ全体にコントローラー、モデル、ビューがあります。モデルとビューは接続されているはずなので、モデルの変更はすぐにビュー反映され、その逆も同様です。模倣は非常に重要です。アーキテクチャは開発者のうそをつくべきではありません。
Jeroen

1
@jeroen頭字語MVCが盗まれ、破損されました。はいMVCにはVMはありませんが、リポジトリやサービスレイヤーもありません。これらのオブジェクトはWebサイトで広く使用されています。OPは「MVCでVMを導入して使用するにはどうすればよいですか」と尋ねていると思います。MVCの新しい意味では、モデルはビジネスロジックが属する場所ではありません。ビジネスロジックは、MVCまたはMVVMを使用するWebまたはデスクトップアプリのサービスレイヤーに属します。モデルという用語は、サービス層との間でやり取りされるビジネスオブジェクトを表します。これらの定義は、MVCの元の説明とは大きく異なります。
サム・

1
@Sam Webサイトの一部であるすべてのものではなく、MVCの一部と呼ぶことができます。MVCの新しい意味はありません。正しい意味と「人々がMVCと混同することとはまったく無関係なもの」という意味があります。モデルがビジネスロジックを担当していると言うことは、ビジネスロジックがモデルでコーディングされていることと同じではありません。ほとんどの場合、モデルはアプリケーションのファサードとして機能します。
Jeroen

MicrosoftのMVCで見られる主な欠陥は、モデルとビューのロックです。それ自体が、過去20年間にN層設計で行われてきたこのすべての分離の全体的な目的に反しています。彼らは私たちの時間を無駄にし、2002年に「WebForms」を使用せざるを得なくなりました。これは、Webの世界に巻き上げられた別のデスクトップ風のモデルです。今、彼らはそれを放棄しましたが、ウェブ開発のためのこの新しいパラダイムのもう一つのデスクトップモデルを再び引き上げました。それまでの間、Googleやその他の企業は、すべてを分離する巨大なクライアント側モデルを構築しています。1998年の古いASP VBScriptは、彼らの真のWeb開発システムだと思っています。
Stokely 2017

11

ビューモデルaは、複数のクラスプロパティを含むことができる単純なクラスです。これを使用して、必要なすべてのプロパティを継承します。たとえば、2つのクラスStudentとSubjectがあります。

Public class Student
{
public int Id {get; set;}
public string Name {get; set;}
}  
Public class Subject
{
public int SubjectID {get; set;}
public string SubjectName {get; set;}
}

ここで、ビューに(MVCで)生徒の名前と被験者の名前のレコードを表示したいのですが、次のような複数のクラスを追加することはできません。

 @model ProjectName.Model.Student  
 @model ProjectName.Model.Subject

上記のコードはエラーをスローします...

これで、1つのクラスを作成して任意の名前を付けることができますが、この形式 "XyzViewModel"を使用すると理解しやすくなります。継承の概念です。次に、次の名前の3番目のクラスを作成します。

public class StudentViewModel:Subject
{
public int ID {get; set;}
public string Name {get; set;}
}

次に、このViewModelをViewで使用します

@model ProjectName.Model.StudentViewModel

これで、ViewのStudentViewModelと継承されたクラスのすべてのプロパティにアクセスできます。


10

大きな例がたくさんありますが、はっきりとクリスピーな方法で説明させてください。

ViewModel =ビューを提供するために作成されるモデル。

ASP.NET MVCビューに複数のモデルを含めることはできないため、複数のモデルのプロパティをビューに表示する必要がある場合は不可能です。ViewModelはこの目的を果たします。

ビューモデルは、ビューに必要なプロパティのみを保持できるモデルクラスです。データベースの複数のエンティティ(テーブル)のプロパティを含めることもできます。名前が示すように、このモデルはビューの要件に固有に作成されます。

ビューモデルのいくつかの例を以下に示します

  • ビューページのエンティティ以外のデータをリストするには、ビューモデルを作成し、データをリストするすべてのエンティティのプロパティを設定できます。それらのデータベースエンティティを結合してビューモデルプロパティを設定し、ビューに戻ってさまざまなエンティティのデータを1つの表形式で表示します。
  • ビューモデルは、ビューに必要な単一のエンティティの特定のフィールドのみを定義できます。

ViewModelは、複数のエンティティへのレコードの挿入、更新にも使用できますが、ViewModelの主な用途は、複数のエンティティ(モデル)の列を単一のビューに表示することです。

ViewModelの作成方法は、モデルの作成と同じです。Viewmodelのビューを作成する方法は、Modelのビューを作成する方法と同じです。

以下は、ViewModelを使用したListデータの小さな例です。

これが役立つことを願っています。


6

ViewModelは、MVCフレームワークの概念の不器用さをパッチする回避策です。3層のModel-View-Controllerアーキテクチャの4番目の層を表します。モデル(ドメインモデル)が適切でなく、ビューに対して大きすぎる(2〜3フィールドを超える)場合は、より小さいViewModelを作成してビューに渡します。


1

ビューモデルは、データの概念モデルです。その使用は、たとえば、サブセットを取得するか、異なるテーブルからデータを結合することです。

特定のプロパティのみが必要な場合があるので、これにより、それらをロードすることのみが可能になり、不要な追加のプロパティはロードできません


1
  • ViewModelには、ビューに表示されるフィールドが含まれます(LabelFor、EditorFor、DisplayForヘルパー用)
  • ViewModelは、データ注釈またはIDataErrorInfoを使用して特定の検証ルールを持つことができます。
  • ViewModelには、異なるデータモデルまたはデータソースからの複数のエンティティまたはオブジェクトを含めることができます。

ViewModelの設計

public class UserLoginViewModel 
{ 
[Required(ErrorMessage = "Please enter your username")] 
[Display(Name = "User Name")]
[MaxLength(50)]
public string UserName { get; set; }
 [Required(ErrorMessage = "Please enter your password")]
 [Display(Name = "Password")]
 [MaxLength(50)]
 public string Password { get; set; } 
} 

ビューでのビューモデルの提示

@model MyModels.UserLoginViewModel 
@{
 ViewBag.Title = "User Login";
 Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm())
{
<div class="editor-label">
 @Html.LabelFor(m => m.UserName)
</div>
<div class="editor-field">
 @Html.TextBoxFor(m => m.UserName)
 @Html.ValidationMessageFor(m => m.UserName)
</div>
<div class="editor-label">
 @Html.LabelFor(m => m.Password)
</div>
<div class="editor-field">
 @Html.PasswordFor(m => m.Password)
 @Html.ValidationMessageFor(m => m.Password)
</div>
<p>
 <input type="submit" value="Log In" />
</p>
</div>
}

アクションでの作業

public ActionResult Login()
{ 
return View();
}
[HttpPost]
public ActionResult Login(UserLoginViewModel user)
{
// To acces data using LINQ
DataClassesDataContext mobjentity = new DataClassesDataContext();
 if (ModelState.IsValid) 
{ 
try
 {
 var q = mobjentity.tblUsers.Where(m => m.UserName == user.UserName && m.Password == user.Password).ToList(); 
 if (q.Count > 0) 
 { 
 return RedirectToAction("MyAccount");
 }
 else
 {
 ModelState.AddModelError("", "The user name or password provided is incorrect.");
 }
 }
 catch (Exception ex)
 {
 } 
 } 
 return View(user);
} 
  1. ViewModelでは、ビュー/ページに表示するフィールド/データのみを配置します。
  2. ビューはViewModelのプロパティを表すため、レンダリングとメンテナンスが簡単です。
  3. ViewModelがより複雑になる場合は、マッパーを使用します。

1

ビューモデルは、ビューにデータをレンダリングするために使用できるクラスです。2つのエンティティPlaceとPlaceCategoryがあり、単一のモデルを使用して両方のエンティティのデータにアクセスする場合は、ViewModelを使用します。

  public class Place
    {
       public int PlaceId { get; set; }
        public string PlaceName { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }
        public string BestTime { get; set; }
    }
    public class Category
    {
        public int ID { get; set; }
        public int? PlaceId { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }
    public class PlaceCategoryviewModel
    {
        public string PlaceName { get; set; }
        public string BestTime { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }

したがって、上記の例では、PlaceとCategoryは2つの異なるエンティティであり、PlaceCategory viewmodelはViewで使用できるViewModelです。


あなたの例はそれほど明確ではありません。上記で述べたことは、ViewModelがデータをビューに接続することです。BlipAjaxのViewModelを見ると、それにぴったり合ったクラスが見つかります。
Herman Van Der Blom、

0

ViewModelsで「ベースライン」Webアプリケーションをセットアップする方法をコードで学習したい場合は、このコードをGitHubでダウンロードすることをお勧めします:https : //github.com/ajsaulsberry/BlipAjax。大規模なエンタープライズアプリケーションを開発しました。これを行うと、このすべての「ViewModel」機能を処理する適切なアーキテクチャをセットアップすることが問題になります。BlipAjaxを使用すると、最初に非常に優れた「ベースライン」が得られると思います。その単純なウェブサイトですが、そのシンプルさは素晴らしいです。私は、アプリケーションで本当に必要なものを指摘するために彼らが英語を使用した方法が好きです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.