繁体   English   中英

在实体框架中编辑对象并将其保存到ASP.NET MVC 2.0中的数据库中

[英]Editing an object in entity framework and saving it to the database in ASP.NET MVC 2.0

所以我知道EF实体跟踪他们自己的更改并在调用savechanges时将它们保存到数据库中,但是这个场景呢......

我有一个旨在编辑博客文章的页面。 它有两种动作方法。

    [HttpGet]
    public ViewResult EditBlogPost(int Id)
    {
        //This action method gets the blog post by the id and returns the edit blog post page.
        BlogPost blogPost = db.BlogPosts.Where(x => x.Id == Id).FirstOrDefault();
        if (blogPost == null)
        {
            ViewData["message"] = "Blog post not found.";
            return View("Result");
        }
        return View("ManageBlogPost", blogPost);
    }

    [HttpPost]
    public ViewResult EditBlogPost(BlogPost blogPost)
    {
        //This one is where I'm having issues. When model binding populates blogPost, is it auto-tracking still? For some reason SaveChanges() doesn't seem to persist the updates.
        if (!ModelState.IsValid)
            return View("ManageBlogPost");
        db.AttachTo("BlogPosts", blogPost); //I tried this method, it seemed to be what I wanted, but it didn't help.
        db.SaveChanges();
        ViewData["message"] = "Blog post edited successfully.";
        return View("Result");
    }

以下是这些回归的观点:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Master.Master" Inherits="System.Web.Mvc.ViewPage<BlogProject.Models.BlogPost>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">

    <% if (Model != null)
       { %>
            <h2>Edit Blog Post</h2>
    <% }
       else
       { %>
            <h2>Add Blog Post</h2>
    <% } %>
    <% using (Html.BeginForm())
       { %>
            <% if (Model != null)
               { %>
                    <%: Html.HiddenFor(x => x.Id)%> <!-- Is this the way to keep un-editable data hidden from the edit form and have them repopulate on the next model bind? What if someone went to modify their POST using something like Fiddler? Couldn't they theoretically edit these fields before the POST? -->
                    <%: Html.HiddenFor(x => x.Date) %>
                    <%: Html.HiddenFor(x => x.Author) %>
                    <%: Html.HiddenFor(x => x.FriendlyUrl) %>
            <% } %>
            Title:<br />
            <%: Html.TextBoxFor(x => x.Title, new { Style = "Width: 90%;" })%>
            <br />
            <br />
            Summary:<br />
            <%: Html.TextAreaFor(x => x.Summary, new { Style = "Width: 90%; Height: 50px;" }) %>
            <br />
            <br />
            Body:<br />
            <%: Html.TextAreaFor(x => x.Body, new { Style = "Height: 250px; Width: 90%;" })%>
            <br />
            <br />
            <input type="submit" value="Submit" />
    <% } %>

</asp:Content>

我在这里有点困惑。 添加博客文章似乎工作正常,但编辑它们是另一个故事。

解决方案不是在编辑操作方法中接受博客文章对象。 相反,做一些看起来像这样的事情:

[HttpPost]
public ViewResult EditBlogPost(int postID)
{
    var post = db.BlogPosts.Single(p => p.PostID = postID);
    TryUpdateModel(post);

    if (!ModelState.IsValid)
        return View("ManageBlogPost");

    db.SaveChanges();

    ViewData["message"] = "Blog post edited successfully.";
    return View("Result");
}

这样,对象附加到上下文,EF可以正确跟踪更改。 UpdateModel方法是一个节省时间的方法,可以自动将表单集合中的字段与模型中的字段进行匹配,并为您更新它们。

以下是UpdateModel的文档: MSDN

实体框架只能跟踪附加到上下文的对象的更改。 上下文生成的对象会自动附加到生成它们的上下文中。 由于您获取的对象是由MVC生成的,因此实体框架不知道哪些值已更新,哪些值尚未更新。

您可以使用几个技巧告诉Entity Framework该项目已被修改。 一种是从上下文中检索实体,在该上下文绑定对象上设置更改的值,然后保存更改。 另一个是做这样的事情来明确地告诉上下文的ObjectStateManager某些属性已经改变:

    /// <summary>
    /// Reattach and mark specific fields on the entity as modified.
    /// </summary>
    /// <param name="objectContext">The context to attach the entity object.</param>
    /// <param name="setName">The string representation of the set that should contain the given entity object.</param>
    /// <param name="entity">The detached entity object.</param>
    /// <param name="modifiedFields">Names of fields that have been modified.</param>
    public static void AttachAsModified(this ObjectContext objectContext, string setName, object entity,
                                        IEnumerable<String> modifiedFields)
    {
        objectContext.AttachTo(setName, entity);
        ObjectStateEntry stateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(entity);
        foreach (String field in modifiedFields)
        {
            stateEntry.SetModifiedProperty(field);
        }
    }

我的团队最终开发了一个框架,该框架自动从上下文加载对象,找出哪些属性与传入的新属性不同,并将这些属性设置为未附加对象提供的值。 这种方法也值得为您考虑。 有可能像AutoMapper这样的东西可以为你做这件事,但我不确定。

编辑

Shea提到使用控制器的UpdateModel方法有效地推迟了属性值的设置,直到从上下文中检索实体对象为止。 这对我来说听起来像是一个好方法(假设你在控制器中有数据访问代码就可以了),但是你可能想要使用覆盖来实现它,这样你仍然可以让方法绑定到同一个对象类型:

[HttpPost]
public ViewResult EditBlogPost(BlogPost blogPost)
{
    //This one is where I'm having issues. When model binding populates blogPost, is it auto-tracking still? For some reason SaveChanges() doesn't seem to persist the updates.
    if (!ModelState.IsValid)
        return View("ManageBlogPost");
    var dbPost = db.BlogPosts.Single(bp => bp.BlogPostId = blogPost.Id);
    UpdateModel(dbPost, "blogPost");
    db.SaveChanges();
    ViewData["message"] = "Blog post edited successfully.";
    return View("Result");
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM