3

I'm trying to solve an issue with the optimistic concurrency control on EF 6. I currently want to catch the DBUpdateConcurrencyException and then refresh the entity. However I am currently getting this Exception:

System.InvalidOperationException: The element at index 0 in the collection of objects to refresh has a null EntityKey property value or is not attached to this ObjectStateManager.

Here is a simplified version of the code that shows the purpose:

using (var dbContextTransaction = dbContext.Database.BeginTransaction(System.Data.IsolationLevel.Serializable))
{
    try
    {
        dbContext.Commit();
    }
    catch(DbUpdateConcurrencyException ex)
    {
        ((IObjectContextAdapter)KnowledgebaseContext).ObjectContext.Refresh(RefreshMode.StoreWins, en);
        dbContextTransaction.Rollback();
    }
}

I couldn't find much on this exception on Google or SO. Any help would be appreciated.

Farhad Alizadeh Noori
  • 2,276
  • 17
  • 22
  • Where is the data you gathered from debugging? How does the model look like? How is it mapped? What is the value of its key property? Is the object attached or not? – Jeroen Vannevel Apr 20 '15 at 17:58
  • 1
    If you are trying to add something to the database, you need to remove it before you are trying to refresh the collection. Now, I think, when you are trying to refresh the collection, there is an entity in the collection which is not yet saved in the database nor attached to the state manager – Saagar Elias Jacky Apr 20 '15 at 18:06
  • @SaagarEliasJacky that's very helpful actually. Do you know of a resource or an article I can use that explains further on this? – Farhad Alizadeh Noori Apr 20 '15 at 18:07
  • 1
    Might want to refer this : http://stackoverflow.com/questions/1746941/objectcontext-refresh – Saagar Elias Jacky Apr 20 '15 at 18:15
  • @JeroenVannevel I actually would be able to find the exact problem myself. I just need more info on the exception as I am rather new to entity framework. The model is complex. I don't know what's meant by "how" it's mapped. In this test scenario I'm not detaching anything. I'm just modifying the same entity from two separate connections. The value of the key property is null hence the exception. – Farhad Alizadeh Noori Apr 20 '15 at 18:19
  • 1
    "the same entity from two separate connections" that might cause a problem because it isn't attached in the right context, yes. Though if you say that the key value is null, what is unclear about the exception? – Jeroen Vannevel Apr 20 '15 at 18:21
  • @JeroenVannevel Well by same entity I mean the same record in the database of course. The whole purpose of catching the exception is to handle the concurrency conflict either automatically or by user supervision right? In this scenario, the conflict occurs when two users update the same entity in some overlapping sequence. Now, if the user A wants to revert their changes, I should refresh. The entity is attached to user A's context. I'm not doing anything to detach it so I don't get the exception. – Farhad Alizadeh Noori Apr 20 '15 at 18:26
  • @SaagarEliasJacky Yeah I had looked at that. That post is really about refreshing all changes where in my case I'm only trying to refresh one. But he is suggesting the same approach really. I haven't been able to find an article that actually goes through some usecases of the concurrency control. Surprised... – Farhad Alizadeh Noori Apr 20 '15 at 18:48
  • Can you post the code where you change/add the record as well as where the context.SaveChanges() is being called, and also where dbContext is defined in the example function? This error usually indicates that the entity object is not attached to the context that is trying to work with it. Also, is this exception occurring on the refresh or the commit()? – AllMadHare Apr 21 '15 at 02:06

1 Answers1

2

I have been able to solve this problem by looking at this and this. The documentation on this feature is rather scarce.

So here is the scenario(we assume that there already is a TimeStamp column the value of which gets updated with each database update):

UserA reads Entity1 and starts making changes. While UserA is making her changes, userB reads Entity1, changes it and saves it to the database. Now UserA wants to save her changes but now by definition, the exact entity that she read no longer exists. The reason for this is that the existence of that entity depends on the TimeStamp column as well which is no longer the same old value. So when I was trying to refresh Entity1 as UserA knew existed, I was getting an exception and I was not able to Refresh either.

Now we'll look at two possible solutions to a concurrency problem for an existing updated entity:

  • Ignore UserA's Changes(store wins): This basically means that one will Refresh the entity from the database. In order to do this, one should overwrite the TimeStamp field for Entity1 in UserA's context with the new one now residing on the database and then try to refresh the information from the server. This way the right entity can be located and retrieved and populated in Entity1 overwriting local changes. Look here for another approach than this.
  • Overwrite changes on the database(client wins): Here, we would overwrite the TimeStamp field and then attempt the update again. By doing so, the EF would no longer detect the update as a conflict and the data on the server is overwritten. The previously referred links contain examples for this case as well.

I don't know exactly for sure why I was getting the exception when using the Refresh method. I switched to using SetValues and GetDatabaseValues and such and my problem was solved.

Community
  • 1
  • 1
Farhad Alizadeh Noori
  • 2,276
  • 17
  • 22