在 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");
}