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; }
    }

View我只需要更新title列,这里是模型代码

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

in Controller,product丢失了除id和之外的其他值title

根据这篇文章,最好的解决方案之一是为此特定表单创建帮助器类,并使用AutoMapper将帮助器映射到原始模型。但在我的情况下(超过 60 个模型)需要额外的资源来实现辅助类。有没有更好的解决方案?

4

1 回答 1

6

在 Controller 中,产品丢失了除 id 和 title 之外的其他值

这很正常。你期待什么?这些是您在 HTML 中包含的唯一字段,<form>因此这些是您唯一希望在控制器操作中返回的内容。这就是 HTML 的工作原理。不要指望奇迹,ASP.NET MVC 不会自动为模型的其他字段设置一些值,即使您没有将它们包含在 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 上提供的无数答案中反复强调和强调这个事实,我仍然看到人们在他们的意见):

所以,是的,定义一个视图模型,它要花多少钱?ACtrl+A在您的模型文件夹中,然后:

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

有那么难吗?

然后导航到您的 NuGet 控制台并键入:Install-Package AutoMapper。然后前往您Application_Start的内部Global.asax并定义您的视图模型和域模型之间的映射(实际上最好稍后在 AutoMapper Profiles 中外部化您的映射):

// 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 回答