Editing an object in an entity infrastructure and storing it in a database in ASP.NET MVC 2.0

So, I know that EF objects track their own changes and save them to the database when calling savechanges, but what about this scenario ...

I have a page for editing a blog post. It has two methods of action.

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

Here is the view they return:

 <%@ 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> 

I'm a little confused. Adding blog posts seems fine, but editing them is another story.

+8
c # asp.net-mvc entity-framework asp.net-mvc-2 entity-framework-4
source share
2 answers

The decision is not to accept the blog post object in your edit action method. Instead, do the following:

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

Thus, the object is context-bound, and EF can correctly track changes. The UpdateModel method is a time saving function that automatically matches the fields in the form collection with the fields in the model and updates them for you.

Here is the documentation for UpdateModel : MSDN

+6
source share

Entity Framework can track changes only for context-bound objects. Objects created by the context are automatically attached to the context that creates them. Since the object you get is created by MVC, the Entity Framework does not know which values โ€‹โ€‹were updated and which were not.

There are a few tricks you can use to tell the Entity Framework that the item has been modified. One of them is to extract the object from the context, set the changed values โ€‹โ€‹for the object associated with the context, and then save the changes. Another is to do something like this to explicitly tell the ObjectStateManager context that some properties have been 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 completed the development of a framework that automatically loads an object from the context, determines which properties differ from the new one that passed, and sets these properties to the values โ€‹โ€‹provided by the unbound object. This approach may be of interest to you. There is a small chance that something like AutoMapper can be done for you, but I'm not sure.

Edit

Shea mentions using the UpdateModel controller method to effectively disable property value settings until you extract an entity object from your context. It sounds like a good approach, like any one for me (assuming everything is fine with the data access code in your controller), but you can use the override to create it so that you can still bind the method to the same Type object:

 [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"); } 
+2
source share

All Articles