使用MVC 3和EF更新模型中的一个字段

Mir*_*ine 2 entity-framework automapper entity-framework-4.1 asp.net-mvc-3

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; }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Dar*_*rov 6

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

这很正常.你有什么期望?这些是您在HTML中包含的唯一字段,<form>因此这些是您希望在控制器操作中获得的唯一内容.这就是HTML的工作原理.不要指望奇迹,即使你没有将它们包含在POST形式中,ASP.NET MVC也不会自动将某些值设置为模型的其他字段.

当然,您可以使用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");
}
Run Code Online (Sandbox Code Playgroud)

尽管如此,请谨慎使用此方法.我不推荐它.攻击者可能通过发送特制请求来设置此类属性.您永远不应将您的域模型传递给您的视图.您应该始终使用视图模型.

为了防止这些大规模注入攻击,您应该保留一个实际允许从视图更新的属性列表:

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

现在只有title属性将由默认模型绑定器更新.


我推荐的实际解决方案当然是使用视图模型(我无法保持代表[在StackOverflow上我提供的大量答案中对这个事实进食和强调,我仍然看到人们使用这些EF自动生成的域模型)观点):

那么,是的,定义一个视图模型,它会花多少钱?一个Ctrl+A在你的模型文件夹,然后:

public class ProductViewModel
{
    public int Id { get; set; }
    [Required]
    public string Title { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这么难吗?

然后导航到您的NuGet控制台并输入:Install-Package AutoMapper.然后向你的内部Application_Start进行操作Global.asax并定义视图模型和域模型之间的映射(实际上,以后在AutoMapper配置文件中外部化您的映射会更好):

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

最后你的控制器动作变为:

[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");
}
Run Code Online (Sandbox Code Playgroud)