0

我是 MVC3 的新手,正在尝试编写一个博客应用程序作为学习工具。

我为博客文章创建了一个数据库对象,并使用带有读/写操作的控制器和使用实体框架控制实体的视图生成了一个控制器。

我在编辑命令时遇到问题。一篇博客文章大约有 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 调用上引发了异常。

如何确保所有属性都返回到第二个编辑方法以正确进行更新?

4

4 回答 4

7

因为当您执行POST时,您没有从表单中发送这些元素的值。解决此问题的一种方法是使用Hidden变量将它们保留在表单中

@using(Html.BeginForm())
{
  @Html.EditorFor(model => model.Title)
  @Html.HiddenFor(model => model.UserId)
  <input type="submit" />
}

我认为干净的解决方案是“不要在视图中使用域模型,使用具有视图必要属性的视图模型。在这种情况下,显然 CreatedDate 不应该是视图应该提供的东西。它应该是代码将提供的东西填充到对象。

所以为此创建一个 ViewModel

public class BlogPostViewModel
{
  public int ID { set;get;}
  public string Title { set;get;}
  public string Description { set;get;}  
}

并使用它来将数据从视图传输到控制器,反之亦然

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库,而不是一一手动映射属性,它可以在一行代码中为您完成此操作!

于 2012-07-03T22:41:12.027 回答
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

于 2012-07-03T22:32:00.010 回答
1

对于您的情况,我认为您应该使用 HtmlHelper 扩展方法“EditorForModel”而不是为每个属性调用“EditorFor”。您在每个属性上使用 EditorFor 使您的生活变得复杂(正如 gred84 所说,它不会在您的上下文中的 HTTP 请求中发布未显示的属性)。

在您的 blog_Post 模型类中,您应该使用属性 [HiddenInput(DisplayValue = false)] 标记您不想编辑的每个属性

然后,您可以简单地拥有(简化 - 没有验证摘要),而不是视图中的所有代码

@using (Html.BeginForm())
{        
   @Html.EditorForModel()
   <input type="submit" value="Save" />   
} 
于 2012-07-03T22:40:22.177 回答
1

模型绑定器将仅填充在 HTTP 请求中发布的属性。您的视图仅包含 Title 和 PostContent。

您需要为每个缺失的属性包含隐藏字段。或者只是 ID 属性,然后对其余部分进行数据库查找。

于 2012-07-03T22:32:46.177 回答