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

女生的网名这么多〃 提交于 2019-12-04 03:38:52

The solution is not to take in the blog post object in your edit action method. Instead, do something that looks like this:

[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");
}

This way the object is attached to the context and the EF can track changes properly. The UpdateModel method is a time saver that automatically matches fields in the form collection to the fields in the model and updates them for you.

Here is the documentation for UpdateModel: MSDN

Entity Framework is only able to track changes on objects that are attached to a context. Objects produced by a context are automatically attached to the context that produces them. Since the object you're getting is produced by MVC, the Entity Framework doesn't know which values have been updated and which haven't been.

There are a couple of tricks you can use to tell Entity Framework that the item has been modified. One is to retrieve the entity from the context, set the changed values on that context-bound object, and then save your changes. Another is to do something like this to explicitly tell the context's ObjectStateManager that certain properties have changed:

    /// <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);
        }
    }

My team ended up developing a framework that automatically loads the object from the context, figures out which properties are different from the new one that got passed in, and sets those properties to the values provided by the unattached object. That approach may be worth looking into for you as well. There is a slight possibility that something like AutoMapper could be made to do this for you, but I'm not sure.

Edit

Shea mentions using the controller's UpdateModel method to effectively put off the setting of the property values until after you have retrieved the entity object from your context. This sounds like as good an approach as any to me (assuming you're okay with having data-access code in your controller), but you may want to use an override to make it so you can still have the method bind to the same object type:

[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");
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!