Entity Framework: ObjectContext observations on caching

I thought that my journey with the Entity Framework was going to be like falling off a cliff, fast and painless but instead it’s been more like driving a Hummer in a Smart car convention, just have to plow my way through.

The latest bump in my path has been the ObjectContext. Just to let it be known, I’m like most kids with a new game, I don’t bother to read the instructions and the throw the controller when I don’t have a clue what I’m doing. My use of the Entity Framework has been no different. So using “o’ skool” standards, I immediately decided that I would have some kind of Singleton context so that I didn’t keep making new ones, and assumedly new connections. Everything seemed fine and dandy until I hit this situation which brought some, at the time, unexplainable or difficult issues.

First off, say you have a Site and Role class, and Role is Many to One with Site. Now let’s say you are creating a page that is used to create/update/delete roles. On this page you have the usual section for updating or creating a new role that involves a Name for the Role and a drop down list to pick a Site from. Now on the update side of it you have basic rules: The Role must have a name, the Role must have a Site, and the Name of the Role can’t already exist in the database for the site. So a new role might look like:

  Role editRole = Role.GetByID(someID);
  editRole.RoleName = txtRoleName.Text;
  editRole.Site = GetSiteFromID(ddlSite.SelectedValue.ConvertTo<Int32>);

The GetByID is just a query that gets the Role by and ID by using the Singleton ObjectContext. (KEY POINT) Beyond that, nothing special except my awesome string convert. Yah. Now the next step before committing to the database is something like:

  if
  (
    context.Any
    (
      role => role.Name == editRole.RoleName
      && role.Site == editRole.Site
    )
  )
  {
     //return error or whatever
  }

So right there I’m checking to see if the new name for the role exists already. Now say I’m updating a role and that I KNOW the new name doesn’t exist. You would think that query would return false and life would be good. Unfortunately two things happened… and they will change your life forever. (EDIT: I realize I could also check to make sure the ID doesn’t match and should do that, but if I had done that initially I wouldn’t have figured out the next part)

First:

I got back an error saying the name already exists. Now as I said, I know it didn’t so how could that happen? Well remember that Singleton ObjectContext idea? First time I grab the Role, it doesn’t exist in the ObjectContext.Role list. So it grabs it from the database and then stores it so that every time I now run that GetByID method, I am actually getting the object from the context NOT THE PERSISTENCE LAYER. (IE database or whatever) Therefore, any changes I make are changes to the object in that context. So what does that mean? Well say I do update the RoleName on that object and then run a query to see if that name exists, guess where that query is looking? Yup the context. And since it’s looking at the same stupid object I just changed, of course the name is going to exist now. Basically I comparing the object to itself to see if it’s name exists. Ouch.

B:

After cancelling, (IE not saving) The grid that I had displaying all the Roles now has that new RoleName in it. What the f? My code specifically says NOT to save unless that (name already exists) query returns false. So how the hell has the name changed? Well just like before, the query that gets the list of Roles is no longer looking at the database but now at the Roles list in the context. That same Roles list that has the Role I changed earlier. Therefore any change to that object will now be reflected when getting the list from the context. Uhg.

So what can you learn about this? Once a list is “loaded” for the first time, it is held in the context until (presumably) you use the Refresh method on the context to refresh the given list. (Not sure how well this works). Therefore, any queries after the first will reflect all changes to the objects in the context, whether you saved to the database or not. The Singleton idea isn’t looking real great right now.