2

Modelクラス

public class product : BaseModel
    {
        [key] public string id { get; set; }
        [Required] public string title { get; set; }
        [Required] public bool valid { set; get; }
        public decimal discount { get; set; }
        public string summery { get; set; }
        public string icon { get; set; }
        public DateTime date { get; set; }
    }

ViewI just need update列のtitleモデル コードは次のとおりです。

@using (Html.BeginForm()){
 @Html.HiddenFor(o => o.id);
 @Html.ValidationSummary(true)
 Title: @Html.EditorFor(model => model.title)
 @Html.ValidationMessageFor(model => model.title)
}

Controller、とproduct以外の値が失われましたidtitle

この投稿によると、最良の解決策の 1 つは、この特定のフォームのヘルパー クラスを作成し、AutoMapperを使用してヘルパーを元のモデルにマップすることです。しかし、私の場合 (60 以上のモデル) では、ヘルパー クラスを実装するために余分なリソースが必要です。より良い解決策はありますか?

4

1 に答える 1

6

コントローラで、製品はIDとタイトル以外の他の値を失いました

それは正常です。何を期待しましたか?これらはHTMLに含めた唯一のフィールドである<form>ため、コントローラーアクションで取り戻すことを期待できるのはこれらだけです。これがHTMLの仕組みです。奇跡を期待しないでください。ASP.NETMVCは、POSTフォームに値を含めていなくても、モデルの他のフィールドにいくつかの値を自動的に設定するだけではありません。

もちろんid、リクエストから受け取ったを使用して、データストアにクエリを実行し、対応するモデルを取得して、それぞれの処理を実行することもできます。

それで:

[HttpPost]
public ActionResult SomeAction(int id)
{
    var product = db.Products.Where(x => x.Id == id);
    if (!TryUpdateModel(product))
    {
        // validation failed => redisplay the view
        return View(model);
    }

    // at this stage the product variable will be updated with the title
    // property coming from the view and all other properties staying untouched
    // => you could persist the changes back to the database
    // I don't remember the syntax of this EF stuff - go read the manual
    // of how to persist the changes of this "product" instance back to the context
    // As far as I remember it was something like "db.SaveChanges" or something.

    ...

    // after a successfull UPDATE redirect in order to follow good practices of the
    // Redirect-After-Post pattern: http://en.wikipedia.org/wiki/Post/Redirect/Get
    return RedirectToAction("Success");
}

ただし、このアプローチでは注意が必要です。私はそれをお勧めしません。攻撃者は、特別に細工されたリクエストを送信することにより、このようなプロパティを設定する可能性があります。ドメインモデルをビューに渡さないでください。常にビューモデルを使用する必要があります。

これらの大量注入攻撃から保護するために、ビューから実際に更新できるプロパティのリストを保持する必要があります。

// ... only update the title property from the view
if (!TryUpdateModel(product, new[] { "title" }))

これで、プロパティのみがtitleデフォルトのモデルバインダーによって更新されます。


私がお勧めする実際の解決策は、もちろんビューモデルを使用することです(StackOverflowでここに提供する膨大な数の回答でこの事実を繰り返し強調し続けることはできませんが、EF自動生成ドメインモデルを使用している人々を今でも見ていますビュー):

それで、ええ、ビューモデルを定義します、それはあなたにいくらかかりますか?Ctrl+AモデルフォルダでAを実行し、次に:

public class ProductViewModel
{
    public int Id { get; set; }
    [Required]
    public string Title { get; set; }
}

大変でしたか?

次に、NuGetコンソールに移動して、次のように入力しますInstall-Package AutoMapper。次に、インに向かいApplication_StartGlobal.asaxビューモデルとドメインモデル間のマッピングを定義します(実際には、後でAutoMapperプロファイルでマッピングを外部化することをお勧めします)。

// gosh this EF crap generates lowercase classes => it sucks so badly
// and violates all C# conventions
Mapper.Map<ProductViewModel, product>();

そして最後に、コントローラーのアクションは次のようになります。

[HttpPost]
public ActionResult SomeAction(ProductViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    var product = db.Products.Where(x => x.Id == id);
    Mapper.Map<ProductViewModel, product>(model, product);

    // at this stage the product variable will be updated with the title
    // property coming from the view and all other properties staying untouched
    // => you could persist the changes back to the database
    // I don't remember the syntax of this EF stuff - go read the manual
    // of how to persist the changes of this "product" instance back to the context
    // As far as I remember it was something like "db.SaveChanges" or something.

    ...

    // after a successfull UPDATE redirect in order to follow good practices of the
    // Redirect-After-Post pattern: http://en.wikipedia.org/wiki/Post/Redirect/Get
    return RedirectToAction("Success");
}
于 2012-09-24T13:40:53.313 に答える