web-dev-qa-db-ja.com

ASP.NET MVC3でのビューモデルの使用

モデルを表示するのは比較的新しいので、モデルの使用に関していくつかの問題が発生しています。これが、ベストプラクティスとは何か疑問に思っている状況の1つです...

ビューに必要なすべての情報をビューモデルに入れています。ここに例があります-エラーを許してください、これは私の頭のてっぺんからコード化されています。

public ActionResult Edit(int id)
{
    var project = ProjectService.GetProject(id);

    if (project == null)
        // Something about not found, possibly a redirect to 404.

    var model = new ProjectEdit();
    model.MapFrom(project); // Extension method using AutoMapper.

    return View(model);
}

画面で1つまたは2つのフィールドの編集しか許可されていない場合、ビューモデルが戻ってきたときに、かなりの量のデータが失われています(本来あるべきことです)。

[HttpPost]
public ActionResult Edit(int id, ProjectEdit model)
{
    var project = ProjectService.GetProject(id);

    if (project == null)
        // Something about not found, possibly a redirect to 404.

    try
    {
        if (!ModelState.IsValid)
            return View(model) // Won't work, view model is incomplete.

        model.MapTo(project); // Extension method using AutoMapper.
        ProjectService.UpdateProject(project);
        // Add a message for the user to temp data.

        return RedirectToAction("details", new { project.Id });
    }
    catch (Exception exception)
    {
        // Add a message for the user to temp data.

        return View(model) // Won't work, view model is incomplete.
    }
}

私の一時的な解決策は、ビューモデルを最初から再作成し、ドメインモデルから再入力し、フォームデータを再適用してから、通常どおりに続行することです。しかし、これはビューモデルパラメータをやや無意味にします。

[HttpPost]
public ActionResult Edit(int id, ProjectEdit model)
{
    var project = ProjectService.GetProject(id);

    if (project == null)
        // Something about not found, possibly a redirect to 404.

    // Recreate the view model from scratch.
    model = new ProjectEdit();
    model.MapFrom(project); // Extension method using AutoMapper.

    try
    {
        TryUpdateModel(model); // Reapply the form data.

        if (!ModelState.IsValid)
            return View(model) // View model is complete this time.

        model.MapTo(project); // Extension method using AutoMapper.
        ProjectService.UpdateProject(project);
        // Add a message for the user to temp data.

        return RedirectToAction("details", new { project.Id });
    }
    catch (Exception exception)
    {
        // Add a message for the user to temp data.

        return View(model) // View model is complete this time.
    }
}

もっとエレガントな方法はありますか?

[〜#〜]編集[〜#〜]

どちらの答えも正しいので、できれば両方を授与します。しかし、試行錯誤の末、彼の解決策が最も無駄がないことがわかったので、うなずきはMJに行きます。

私もヘルパーを使うことができます、ジミー。表示する必要があるものをビューバッグ(またはビューデータ)に追加すると、次のようになります...

ViewBag.Project= project;

その後、次のことができます...

@Html.LabelFor(model => ((Project)ViewData["Project"]).Name)
@Html.DisplayFor(model => ((Project)ViewData["Project"]).Name)

ちょっとしたハックで、ドメインモデルをSystem.ComponentModel.DisplayNameAttributeで装飾する必要がある場合もありますが、私はすでにそうしています。

電話したい...

@Html.LabelFor(model => ViewBag.Project.Name)

しかし、動的は式に問題を引き起こします。

17
Adam Boddington

試行錯誤(コード化してから嫌い)を学んだ後、現在私が好むアプローチは次のとおりです。

View-models onlyを使用して入力フィールドをバインドします。したがって、あなたの場合、ビューが2つのフィールドのみを編集している場合、ビューモデルには2つのプロパティしかありません。ビューにデータを入力するために必要なデータ(ドロップダウンリスト、ラベルなど)には、動的なViewBagを使用します。

ビューを表示すること(つまり、ビューに表示する必要があるものをすべて入力すること)と、投稿されたフォーム値をキャプチャすること(バインド、検証など)は、2つの別個の関心事であると私は信じています。また、ビューにデータを入力するために必要なデータと、ビューからポストバックされたデータを混在させると、煩雑になり、状況が正確に作成されることがよくあります。部分的に実装されたオブジェクトが渡されるのは嫌いです。

Automapper(ドメインオブジェクトを動的ViewBagにマッピングするため)でこれがどのように機能するかはわかりませんが、使用したことがありません。動作する可能性のあるDynamicMapメソッドがあると思いますか?投稿された強く型付けされたViewModelをDomainオブジェクトに自動マッピングすることに問題はありません。

13
MJ Richardson

私が正しく理解していれば、ビューモデルはおそらくドメインエンティティに非常によく似ています。特定のフィールドのみが編集可能であるため、ビューモデルはほとんど空に戻る可能性があるとおっしゃいました。

編集(または表示)できるフィールドが少ないビューがあるとすると、これらはビューモデルで使用できるようにする必要があるonlyフィールドです。私は通常、ビューごとに1つのビューモデルを作成し、コントローラーまたはサービスのいずれかがユーザーの入力を処理し、検証を実行した後、ドメインエンティティにマップし直します。

ここ は、役立つと思われるビューモデルのベストプラクティスに関するスレッドです。

編集:編集/ POSTアクションで編集/ GETアクションが提供するものとは異なるビューモデルを受け入れることもできます。モデルバインダーがそれを理解できる限り、これは機能するはずだと思います。

8
Andrew Whitaker