3

もちろん、これを行うには多くの方法がありますが、アプローチの利点と欠点について少しフィードバックを求めたいと思いました.

まず、NerdDinner チュートリアルの編集アクションは次の形式です (たとえば、フォーム A)。

[HttpPost]
public ActionResult Edit(int id, FormCollection collection) {

ViewModels を自分のビューに合わせて形作ると、フォーム B のアプローチになるように思えます。

[HttpPost]
public ActionResult Edit(MyViewModel mvm) {

より良い、よりクリーンなアプローチのようです。次に、VM のプロパティをモデルのプロパティにマップして保存します。ただし、この ViewModel に、コンストラクターを介して初期化される他のエンティティが埋め込まれている場合 (nerddinner チュートリアルなど)、既定のコンストラクターがない場合、この編集アクションは失敗し、最初のアプローチを使用する必要があります。

では、最初の質問は、通常はフォーム B の方が優れていることに同意しますか? 欠点はありますか?

次に、フォーム B を使用する場合、デコレータの型の検証を ViewModel に含める必要があるようです。ViewModels にエンティティを埋め込み、エンティティ レベルのみで検証を行う利点はありますか?

4

2 に答える 2

1

これはかなり一般的な SO の質問です。

最初の質問は、通常はフォーム B の方が優れていることに同意しますか?

フォーム Bを使用しないのは、ファイルをアップロードするときだけです。そうでなければ、誰もForm Aを使用する必要はないと思います。人々がフォーム Aを使用する理由は、MVC の ASP.Net バージョンの機能を理解していないためだと思います。

第 2 に、フォーム B を使用する場合、デコレータ タイプの検証を ViewModel に含める必要があるようです。

並べ替え/依存します。例を挙げます:

public IValidateUserName
{
  [Required]
  string UserName { get; set; }
}

public UserModel
{
  string UserName { get; set; }
}

[MetadataType(typeof(IValidateUserName))]
public UserValiationModel : UserModel
{
}

検証デコレーターはインターフェースにあります。派生クラスで MetadataType を使用して、派生型を検証しています。再利用可能な検証が可能で、MetadataType/Validation は ASP.NET コア機能の一部ではないため、ASP.Net (MVC) アプリケーションの外部で使用できるため、私は個人的にこの方法が気に入っています。

ViewModels にエンティティを埋め込む利点はありますか..

はい、基本的なモデルをビューに渡さないように最善を尽くしています。これは私がしないことの例です:

public class person { public Color FavoriteColor { get; set; } }

ActionResult Details()
{
  Person model = new Person();
  return this.View(model);
}

ビューにさらにデータを渡したい場合 (パーシャル データまたはレイアウト データ) はどうなりますか? ほとんどの場合、その情報は Person 関連ではないため、 Person モデルに追加しても意味がありません。代わりに、私のモデルは通常次のようになります。

public class DetailsPersonViewModel()
{
  public Person Person { get; set; }
}

public ActionResult Details()
{
  DetailsPersonViewModel model = new DetailsPersonViewModel();
  model.Person = new Person();
  return this.View(model);
}

DetailsPersonViewModelこれで、Person が知っている以上にそのビューが必要とする必要なデータを追加できます。たとえば、Person がお気に入りを選択するためのすべての色を含む for を表示するとします。可能なすべての色は人物の一部ではなく、人物モデルの一部であってはならないため、それらを DetailPersonViewModel に追加します。

public class DetailsPersonViewModel()
{
  public Person Person { get; set; }
  public IEnumerable<Color> Colors { get; set; }
}

..エンティティレベルのみで検証を維持しますか?

System.ComponentModel.DataAnnotationsプロパティのプロパティを検証するように設計されていないため、次のようにします。

public class DetailsPersonViewModel()
{
  [Required(property="FavoriteColor")]
  public Person Person { get; set; }
}

存在しませんし、意味がありません。検証が必要なエンティティの検証を ViewModel に含める必要がない理由。

デフォルトのコンストラクターがなく、最初のアプローチを使用する必要がある場合、この編集アクションは失敗します。

正解ですが、ViewModel または ViewModel 内のエンティティにパラメーターなしのコンストラクターがないのはなぜですか? 悪い設計のように聞こえますが、これに何らかの要件がある場合でも、ModelBinding によって簡単に解決できます。次に例を示します。

// Lets say that this person class requires 
// a Guid for a constructor for some reason
public class Person
{
  public Person(Guid id){ }
  public FirstName { get; set; }
}

public class PersonEditViewModel
{
  public Person Person { get; set; }
}

public ActionResult Edit()
{
  PersonEditViewModel model = new PersonEditViewModel();
  model.Person = new Person(guidFromSomeWhere);

  return this.View(PersonEditViewModel);
}

//View 
@Html.EditFor(m => m.Person.FirstName)

//Generated Html
<input type="Text" name="Person.FirstName" />

これで、ユーザーが新しい名前を入力できるフォームができました。このコンストラクターで値を取得するにはどうすればよいでしょうか? シンプルです。ModelBinder はバインド先のモデルを気にせず、HTTP 値を一致するクラス プロパティにバインドするだけです。

[MetadataType(typeof(IPersonValidation))]
public class UpdatePerson
{
  public FirstName { get; set; }
}  

public class PersonUpdateViewModel
{
  public UpdatePerson Person { get; set; }
}

[HttpPost]
public ActionResult Edit(PersonUpdateViewModel model)
{
  // the model contains a .Person with a .FirstName of the input Text box
  // the ModelBinder is simply populating the parameter with the values
  // pass via Query, Forms, etc

  // Validate Model

  // AutoMap it or or whatever

  // return a view
}
于 2012-12-20T18:21:47.263 に答える
0

私はまだ NerDinner プロジェクトを見ていませんが、通常、アクションの POST で ViewModel を使用しないようにし、代わりに「フォーム」の要素のみを送信します。

たとえば、ViewModel にある種のドロップダウンで使用されるディクショナリがある場合、ドロップダウン全体は送信されず、選択された値のみが送信されます。

私の一般的なアプローチは次のとおりです。

[HttpGet]
public ActionResult Edit(int id) 
{
     var form = _service.GetForm(id);

     var pageViewModel = BuildViewModel(form);

     return View(pageViewModel);
}

[HttpPost]
public ActionResult Edit(int id, MyCustomForm form) 
{
     var isSuccess = _service.ProcessForm(id);    

      if(isSuccess){
         //redirect
      }

      //There was an error. Show the form again, but preserve the input values
      var pageViewModel = BuildViewModel(form);

      return View(pageViewModel);
}

private MyViewModel BuildViewModel(MyCustomForm form)
{
     var viewModel = new MyViewModel();

     viewModel.Form = form;
     viewModel.StateList = _service.GetStateList();

    return viewModel;
}
于 2012-12-20T17:03:34.213 に答える