ASP.Net MVC3缺少模型属性

时间:2012-07-03 22:27:37

标签: c# sql-server asp.net-mvc-3 entity-framework

我是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调用中显然会抛出异常,因为缺少必需的外键。

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

4 个答案:

答案 0 :(得分:7)

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

将它们保留在表单中
@using(Html.BeginForm())
{
  @Html.EditorFor(model => model.Title)
  @Html.HiddenFor(model => model.UserId)
  <input type="submit" />
}

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

因此,为此

创建一个ViewModel
public 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" />   
}