I am reading the following tutorial about entity framework 6 Link. And inside the section named â€Adding an Edit Page for Instructorsâ€, the author wrote the following code inside the Post edit action method:-
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var instructorToUpdate = db.Instructors
.Include(i => i.OfficeAssignment)
.Where(i => i.ID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "",
new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
{
try
{
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
{
instructorToUpdate.OfficeAssignment = null;
}
db.Entry(instructorToUpdate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
return View(instructorToUpdate);
}
This code will cover these three conditions:-
So does this means that
db.Entry(instructorToUpdate).State = EntityState.Modified;
will cause an insert statement to be performed for the OfficeAssignment record incase the Instructor did not have a prevouse OfficeAssignment object ? and what is the rule that govern this ?
here is the complete model diagram:-
DbContext.Entry
method is used to do an explicit loading, that means that It gives you access to all the information that the DbContext has about an entity. This goes beyond the values that are stored in the properties of the actual entity and includes things such as the state of the entity and the original values for each property when it was retrieved from the database.
When you call the TryUpdateModel
method, it will update the properties (that you pass their names as a parameter) with values from the model binder. One of these properties is OfficeAssignment
, wich is updated too. If in your view you don't enter a Location
, then you don't have reason to create a new OfficeAssigment
(that's way you need to do instructorToUpdate.OfficeAssignment = null;
because even when you don't enter a new Location
, you will have a instance of OfficeAssignment
). If you add a new Location
, you are going to create a new OfficeAssignment
, and if you modified the Location
, then you are going to modified its value.
When you do this:
db.Entry(instructorToUpdate).State = EntityState.Modified;
You are going to set a flag on the entity indicating it has been changed. When the SaveChanges
method is called, the Modified
flag causes the Entity Framework to create SQL statements to update the database row. All columns of the database row will be updated, including those that the user didn't change, and concurrency conflicts are ignored. To understand better what happend, you can look the Instructor
instance like a tree. Code First recognizes that you have a navegation property, so it need to be updated or insterted(depending on the case). If the OfficeAssignment
have an Id
different of the default(int)
(I'm pressuming that is an interger), then it will be updated, and in other case, it will be inserted.
There are basically two ways an entity can be persisted through EF.
A. Add it directly to the Dbset with the additional relationships you want it to have.
Entity e = new Entity();
e.ForeignEntityId = 123;
context.Entities.Add(e);
context.SaveChanges();
B. Attach it to an existing entity and if that entity is/was untracked, mark that entity as `Modified.
Entity e = new Entity();
ForeignEntity fe = context.Find(...);
//Only needed if 'fe' was untracked
//context.Entry(fe).State = EntityState.Modified;
fe.Entity = e;
context.SaveChanges();
The way presented in your question is the second way. It's all about getting the "new" object to be present in the object graph that represents all tracked EF entities from your DB.