我是MVC3的新手,我正在尝试将博客应用程序编写为学习工具。
我为博客文章创建了一个数据库对象,并使用带有读/写操作的Controller和使用Entity Framework控制实体的视图生成了一个控制器。
我遇到了编辑命令的麻烦。博客文章大约有6个属性,但我只想允许编辑修改帖子的标题和内容。我的代码如下:
public ActionResult Edit(int id)
{
blog_Post blog_post = db.blog_Post.Find(id);
return View(blog_post);
}
//
// POST: /Post/Edit/5
[HttpPost]
public ActionResult Edit(blog_Post blog_post)
{
if (ModelState.IsValid)
{
db.Entry(blog_post).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(blog_post);
}
@model BlogVersion1._0.blog_Post
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>blog_Post</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.PostContent)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.PostContent)
@Html.ValidationMessageFor(model => model.PostContent)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
出现的问题出现在公共ActionResult Edit(blog_Post blog_post)方法中。在Edit(int id)方法中,我放了一个断点,我可以看到blog_post正在传递给视图(包括填充的所有属性)。
但是返回到[HttpPost]方法的blog_post缺少UserId,DateCreated等的属性。在db.SaveChanges调用中显然会抛出异常,因为缺少必需的外键。
如何确保将所有属性都返回到第二个编辑方法以正确进行更新?
答案 0 :(得分:7)
因为当您执行 POST 时,您没有从表单中发送这些元素的值。解决此问题的一种方法是使用Hidden
变量
@using(Html.BeginForm())
{
@Html.EditorFor(model => model.Title)
@Html.HiddenFor(model => model.UserId)
<input type="submit" />
}
我认为干净的解决方案是“不要在视图中使用域模型,使用带有必要属性的ViewModel。在这种情况下,显然CreatedDate不应该是View应该是它应该是代码填充到对象的东西。
因此,为此
创建一个ViewModelpublic class BlogPostViewModel
{
public int ID { set;get;}
public string Title { set;get;}
public string Description { set;get;}
}
并使用它将数据从View传输到控制器,反之亦然
public ActionResult Edit(int id)
{
var domainObject=repo.GetPost(id);
var viewModel=new BlogPostViewModel();
viewModel.ID=domainObject.ID;
viewModel.Title=domainObject.Title;
//map other REQUIRED properties also
return View(viewModel);
}
您的观点将强烈输入此
@model BlogPostViewModel
@using(Html.BeginForm())
{
@Html.EditorFor(model => model.Title)
@Html.HiddenFor(model => model.Description)
<input type="submit" />
}
在POST操作中,将其映射回域对象并保存
[HttpPost]
public ActionResult Edit(BlogPostViewModel model)
{
if(ModelState.IsValid)
{
var domainObject=new blog_Post();
domainObject.Title=model.Title;
domainObject.ModifiedDate=DateTime.Now;
//set other properties also
repo.Update(domainObject);
return RedirecToAction("Success");
}
return View(model);
}
您可以考虑使用AutoMapper库来代替手动映射属性,而不是在一行代码中为您执行此操作!
答案 1 :(得分:3)
只需为所有其他不可编辑的属性添加隐藏字段。
@Html.HiddenFor(model => model.Id)
这些字段将包含在POST中,因此,模型绑定器会正确地将它们放入您的blog_post实例中。
另一方面,您应该使用视图模型 - 简单的POCO类,它们将成为您视图的模型。不建议直接使用实体模型。
以下是一些信息:
ASP.NET MVC ViewModel Pattern
http://stephenwalther.com/archive/2009/04/13/asp-net-mvc-tip-50-ndash-create-view-models.aspx
答案 2 :(得分:1)
模型绑定器仅填充HTTP请求中POST的属性。您的视图仅包含标题和PostContent。
您需要为每个缺少的属性添加隐藏字段。或者只是ID属性,然后对其余部分进行数据库查找。
答案 3 :(得分:1)
对于您的情况,我认为您应该使用HtmlHelper扩展方法“EditorForModel”而不是为每个属性调用“EditorFor”。你在每个属性上使用EditorFor使你的生活变得复杂(正如gred84所说,它不会在你的上下文中发布HTTP请求中的非显示属性)。
在blog_Post模型类中,您应该使用属性[HiddenInput(DisplayValue = false)]
标记您不想编辑的每个属性然后,您可以简单地(简化 - 没有验证摘要)
代替视图中的所有代码@using (Html.BeginForm())
{
@Html.EditorForModel()
<input type="submit" value="Save" />
}