DBContext, state and original values

2019-05-06 20:29发布

问题:

I'm working with ASP.NET MVC3 using EF and Code First.

I'm writing a simple issue tracker for practice. In my Controller I have a fairly standard bit of code:

[HttpPost]
public ActionResult Edit(Issue issue) {
    if (ModelState.IsValid) {
        dbContext.Entry(issue).State = EntityState.Modified
        .....
    }
}

Question part 1 I'm trying to get my head around how the dbcontext works - Before I've set the State on the dbContext.Entry(issue), I assume my issue object is detached. Once I set the state to be modified, the object is attached - but to what? the dbContext or the database? I'm kind of missing what this (attaching) actually means?

Question part 2 For argument's sake, let's say I decide to set the "Accepted" field on my issue. Accepted is a boolean. I start with it being false, I'm setting it to true in the form and submitting. At the point that my object is attached, what is the point of the OriginalValues collection? for example if I set a breakpoint just after setting EntityState.Modified but before I call SaveChanges() I can query

db.Entry(issue).OriginalValues["Accepted"]

and this will give me the same value as simply querying the issue object that has been passed in to the Edit....i.e. it is giving the same result as

issue.Accepted

I'm clearly missing something because the documentation says "The original values are usually the entity's property values as they were when last queried from the database." But this is not the case because the database is still reporting Accepted as being false (yeah, I noted the word "usually" in the docs but my code is all pretty much standard generated by MS code so....). So, what am I missing? what is actually going on here?

回答1:

The context can work only with attached entities. The attaching means that context know about the entity, it can persists its data and in some cases it can provide advanced features like change tracking or lazy loading.

By default all entities loaded from the database by the context instance are attached to that instance. In case of web applications and other disconnected scenarios you have a new context instance for every processed HTTP request (if you don't you are doing a big mistake). Also your entity created by model binder in HTTP POST is not loaded by that context - it is detached. If you want to persist that entity you must attach it and inform context about changes you did. Setting state to Entry to Modified will do both operations - it will attach entity to the context and set its global state to Modified which means that all scalar and complex properties will be updated when you call SaveChanges.

So by setting state to Modified you have attached the entity to the context but until you call SaveChanges it will not affect your database.

OriginalValues are mostly useful in fully attached scenarios where you load entity from the database and make changes to that attached entity. In such case OriginalValues show property values loaded from database and CurrentValues show actual values set by your application. In your scenario context doesn't know original values. It thinks that original values are those used when you attached the entity.