Entity Framework Optimistic Concurrency Exception Handling - c#

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.

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.

Related

How do I compare an entity in my local context with the one in the database in EF Core?

EF Core 6 and .NET 6.
Suppose all my entities have a LastUpdateAt property, which is a DateTime that gets updated every time an entity is added or modified.
I get an entity from the context and show it to the user (web page, WPF window, whatever). At some point, the user clicks a Save button.
Before I save, I want to check if the entity has been updated by someone else since I got my copy. However, I'm struggling to see how to do this.
If I query the context, it just gives me back the entity I already have (including any changes my user has made).
If I refresh the entity, it overwrites the one in my context, losing my user's changes.
How do I check if the database version has a newer time stamp than the one in my context?
Thanks
Moving the discussion here since I need to paste longer text. In this article it's said, during SaveChanges(), if the DATABASE version was modified in the mean time it will throw DbUpdateConcurrencyException. In that exception you have all 3 values and YOU can decide on how to resolve the conflict:
Resolving a concurrency conflict involves merging the pending changes from the current DbContext with the values in the database. What values get merged will vary based on the application and may be directed by user input.
There are three sets of values available to help resolve a concurrency conflict:
Current values are the values that the application was attempting to write to the database.
Original values are the values that were originally retrieved from the database, before any edits were made.
Database values are the values currently stored in the database.
If you are loading an entity, keeping a DbContext instance open, updating that entity, then saving to the same DbContext instance then by default you are relying on EF to manage concurrency. This follows a "last in wins". You can let EF manage the concurrency by adding a [ConcurrencyCheck] on the LastUpdateAt property or using a Row Version via [Timestamp]. This will cause EF to fail updating if the underlying data has been updated. From there you have to decide how you want to handle it.
If you want to perform the concurrency check yourself then there are a couple of options.
Structure your code to shorten the lifespan of the DbContext using either detached entities or projected View Models. This will generally have flow-on benefits to your code performance as the original longer-lived DbContext can easily find ways to cause bloat, or accumulate "poisoned" entities if alive too long. Automapper is a great tool to assist here where you can use ProjectTo to get the view models, then Map(source, destination) to copy the values across afterward. In this way you load the data including the last modified at value, make your changes, then when saving, you load the data, validate the modified at etc. then copy the values across and save.
Scope a DbContext instance to check the data before saving.
.
private DateTime getFooLastUpdateAt(int fooId)
{
using(var context = new AppDbContext())
{
var lastUpdateAt = context.Foos
.Where(x => x.FooId == fooId)
.Select(x => x.LastUpdateAt)
.Single();
return lastUpdateAt;
}
}
This could use an injected DbContext factory or such to create the DbContext instance..

C# EF 6 CurrentValues.SetValues cannot change Object's Key Information

I have seen other questions about this same error, but I am unable to correct the error with those suggestions in my code; I think that this is a different problem and not a duplicate.
I have an app that makes a series of rules, of which the user can set properties in the GUI. There is a table of Rules in a connected database, with the primary key on the Rule.Id. When the user saves changes to a rule, the existing rule gets "IsActive=0" to hide it, then a new database record is made with the properties from the GUI written to the database. It looks to the user as though they have edited the rule, but the database actually sees a new rule reflecting the new properties (this allows for a history to be kept), connected to the old rule by another reference field.
In the C# code for the app, the View Model for each rule contains an EF Rule object property. When the user clicks "save" I use the parameters set in the view to build the ruleViewModel.Rule for each ruleViewModel they want to save, with porperties matching the GUI. The MainViewModel contains the DbContext object called dbo, so I use the ruleViewModel.Rule to write to the mainViewModel.dbo.Entry which I save to the Entity Framework. Here are the three basic steps performed for each saveable Rule View Model:
// get the rule from the GUI and use it to make sure we are updating the right rule in EF (which is connected to the mainViewModel)
var dboItem = ruleViewModel.MainViewModel.dbo.Rules.Single(r => r.Id == ruleViewModel.Rule.Id);
// set the values in the EF item to be those we got from the GUI
ruleViewModel.MainViewModel.dbo.Entry(dboItem).CurrentValues.SetValues(ruleViewModel.Rule);
// Save the differences
ruleViewModel.MainViewModel.dbo.SaveChanges();
If the user only saves a single rule, it all works fine, but if they subsequently try to save another, or if they save more than one at once, they get the following error, which is return by the ..SetValues(..) line:
Message = "The property 'Id' is part of the object's key information and cannot be modified. "
I see from other questions on this subject that there is a feature of EF that stops you from writing the same object twice to the database with a different Id, so this error often happens within a loop. I have tried using some of the suggestions, like adding
viewModel.MainViewModel.dbo.Rules.Add(dboItem);
and
viewModel.MainViewModel.dbo.Entry(dboItem).Property(x => x.Id).IsModified = false;
before the SaveChanges() command, but that has not helped with the problem (not to mention changing the function of the code). I see that some other suggestions say that the Entry should be created within the loop, but in this case, the entries are all existing rules in the database - it seems to me (perhaps erroneously) that I cannot create them inside the save loop, since they are the objects over which the loop is built - for each entity I find, I want to save changes.
I'm really confused about what to do and tying myself increasingly in knots trying to fix the error. It's been several days now and my sanity and self-esteem is beginning to wane! Any pointers to get me working in the right direction to stop the error appearing and allow me to set the database values would be really welcome as I feel like I have hit a complete dead end! The first time around the loop, everything works perfectly.
Aside from the questionable location of the DbContext and view models containing entities, this looks like it would work as expected. I'm assuming from the MVVM tag that this is a Windows application rather than a web app. The only issue is that this assumes that the Rule entity in your ruleViewModel is detached from the DbContext. If the DbContext is still tracking that entity reference then getting the entity from the DbContext again would pass you back the same reference.
It would probably be worth testing this once in a debug session. If you add the following:
var dboItem = ruleViewModel.MainViewModel.dbo.Rules.Single(r => r.Id == ruleViewModel.Rule.Id);
bool isReferenceSame = Object.ReferenceEquals(dboItem, ruleViewModel.Rule);
Do you get an isReferenceSame value of True or False? If True, the DbContext in your main view model is still tracking the Rule entity and the whole get dboItem and SetValues isn't necessary. If False, then the ruleViewModel is detached.
If the entities are attached and being tracked then edits to the view model entities would be persisted when you call a SaveChanges on the DbContext. (No load & SetValues needed) This should apply to single or multiple entity edits.
If the entities are detached then normally the approach for updating an entity across DbContext instances would look more like:
var context = mainViewModel.dbo;
foreach( var ruleViewModel in updatedRuleViewModels)
{
// This should associate the Entity in the ruleViewModel with the DbContext and set it's tracking state to Modified.
context.Entry(ruleViewModel.Rule).State = EntityState.Modified;
}
context.SaveChanges();
There are a couple of potential issues with this approach that you should consider avoiding if possible. A DbContext should be kept relatively short lived, so seeing a reference to a DbContext within a ViewModel is a bit of a red flag. Overall I don't recommend putting entity references inside view models or passing them around outside of the scope of the DbContext they were created in. EF certainly supports it, but it requires a bit more care and attention to assess whether entities are tracked or not, and in situations like web applications, opens the domain to invalid tampering. (Trusting the entity coming in where any change is attached or copied across overwriting the data state)

Entity Framework update is not updating database table

I have code which I thought was working for Entity Framework update sql server table. I do not get an error upon stepping through the code.
var newsToUpdate = db.tblTips.Find(id);
//if(TryUpdateModel(newsToUpdate, "",))
try
{
db.SaveChanges();
}
catch (RetryLimitExceededException)
{
ModelState.AddModelError("", "unable to save changes");
}
Notice I have that if(TryUpdateModel(... line I don't recall if I had used that when records in the database table DID update.
I can see that the Model has the correct id
DB table is not updated.
What can I do to figure out WHY in C# visual studio as to it not updating the record? There are no errors.
UPDATE:
my model is tblTips , signature on my method is
public ActionResult AddNews(tblTips tips, int? groupid, int? id)
Seems that the update page is not knowing about other columns and trying to update all the columns of which it is trying to update a column to null in which in the db table that column is a "not null"
Is it "ok" ( yes yes i know i should use a viewmodel) to do the following to exclude a column from the update?
db.Entry(tips).State = EntityState.Modified;
db.Entry(tips).Property(x => x.createdby).IsModified = false;
db.SaveChanges();
I guess I should not believe everything I read on Stackoverflow , that failed above with
Attaching an entity of type 'BRM.Data.Models.tblTips' failed because
another entity of the same type already has the same primary key
value. This can happen when using the 'Attach' method or setting the
state of an entity to 'Unchanged' or 'Modified' if any entities in the
graph have conflicting key values. This may be because some entities
are new and have not yet received database-generated key values. In
this case use the 'Add' method or the 'Added' entity state to track
the graph and then set the state of non-new entities to 'Unchanged' or
'Modified' as appropriate.
As it stands, your code now says "get this thing out of the database, then save any changes I just made", but it never actually changes the thing you got out of the database.
Most likely your problem came when you commented out the call to TryUpdateModel(), which ASP.NET MVC uses to make modifications to the given object based on parameters passed into the HTTP request.
I highly recommend using a version control system to make it easier to see what you've changed at any given point in time, and undo things you changed by mistake.
If you look at the documentation for DbContext.SaveChanges, you'll see it mentions the following...
Saves all changes made in this context to the underlying database.
Since you've made no changes (in the shown code), the expected result is happening.
Also, the api public virtual int SaveChanges() suggests you can get the number of records updated from SaveChanges, e.g.
var recordsUpdated = db.SaveChanges();
In the code as you posted it, there are no changes made to any data. Entity Framework correctly determines this and does not issue any update statements.
It appears that the TryUpdateModel call is what makes changes, though I wouldn't be able to tell without looking at its code. Since this call is commented out, the changes aren't happening.

LINQ-to-SQL + One-to-Many + DataBinding deleting

I use LINQ-to-SQL to load data from a database that has two tables in a one-to-many relationship (one Recipe has many Ingredients).
I load a Recipe and LINQ retrieves Ingredient objects into an EntitySet that is binded into a ListBox.
If I want to delete some Ingredients off a Recipe, I get a "An attempt was made to remove a relationship between a Recipe and a Ingredient. However, one of the relationship's foreign keys (Ingredient.RecipeID) cannot be set to null.
I SOLVED this problem using the well known solution by adding 'DeleteOnNull="true"' to the DBML file. But adding this setting only removes the problem when we are deleting Ingredient objects that were retrieved from the DB.
The problem is with the Ingredient objects that were created in code (added to a Recipe) and added to the EntitySet collection of Ingredients and then deleted BEFORE SubmitUpdates is called. Then, the same exception happens again. This usually happens on a new, unsaved recipe when user is adding ingredients to it, makes a mistake and erases an ingredient off a recipe. I added the DeleteOnNull to both 'Association Name="Recipe_Ingredient"' lines in DBML.
How am I supposed to remove such objects? The only solution I see at the moment is that I would load the ingredients into a collection not under the DataContext and then when saving, delete all ingredients off a recipe and add then again from that cache..
try
{
// Needed for existing records, but will fail for new records
yourLINQDataContext.Ingredients.DeleteOnSubmit(ingredient);
}
catch (Exception)
{
// Swallow
}
yourRecipeObject.Ingredients.Remove(ingredient);
It seems that you're looking for something that I was looking for myself just a few days back when I asked "How do I design backing data types for a databound WPF dialog with Ok/Cancel buttons?".
The answer is an intriguing post from Paul Stovell describing a sample IEditable adapter for Linq to Sql. This will let you create your desired "Apply/Cancel" semantics in a generalized manner without completely dissociating yourself from the underlying ORm-generated classes through a full custom-written layer.
It's a pretty slick trick, overall, that will essentially let you sidestep the problems you're fighting right now. :)
On a different note, I'm curious as to why your recipe to ingredient relationship is 1:n instead of m:n. Is it for simplicity's sake? I use garlic in a lot of recipes. :)
// Create new entities
Cart c = new Cart();
CartEntry ce = new CartEntry();
ce.Cart = c;
// Delete the entry
c.CartEntries.Remove(ce);
dc.Cartentries.Attach(ce);
dc.CartEntries.DeleteOnSubmit(ce);
// Insert the cart into database
dc.Carts.InsertOnSubmit(c);
dc.SubmitChanges();
Explaination of the issue: Both entities, c and ce, are not related to a data context - they are not being tracked. EntitySet.Remove() (first delete line) only removes the relation between c and ce. While c can exist without associated cart entries, ce can't exist without an assiciated cart because of a foreign key constraint. When submitting changes to the database, the disconnected ce is dealt with as well, causing a constraint violation and the exception.
In order to get rid of that untracked and disconnected cart entry you need to attach it to your data context (causing it to be tracked) and then mark it for delete on submit. The moment you submit your changes the cart entry will be deleted properly and not cause the exception.
For more details on that issue check this out:
http://msdn.microsoft.com/en-us/library/bb546187%28v=VS.100%29.aspx
you need to decouple the save code from the events in your GUI, it seems like you're a little to eager to save things to the db before the dust has settled and you're queuing and removing things from the db that never got there in the first place, it would be best if you could identify a point when the user will "commit" their changes, and at that moment, process the full condition of the GUI - this will save you a bunch of spaghetti code.
I would also be curious to know if your entities have autonumber IDs or if you're using some other ID mechanism. You're probably sending DELETEs to the database for the as-yet-uncommitted Ingredient records, if those include NULL IDs, I think the linq could get nasty.
Have you hooked up a textwriter to your DataContext.Log to see what sorts of SQL is generated just before you get your exeception?
Thank you for your answer, I will examine the posts and see what I can do. I must say I'm surprised to even see this problem occuring, it seems quite natural to me that one could add records to the LINQ-provided "cache" of data, then decide to erase some of them and then commit. Change tracking should be able to handle that. I just starting with LINQ so I might be doing a stupid mistake somewhere in the code (wouldn't be the first).
On the other note: You are quite correct that garlic can belong to many recipes (not my coctail recipes thought!). I actually model that with an Article object/table. But for a recipe, you need quantities. So in my model, you have a Recipe that has 1:n Ingredients, each of them having a Quantity, a 1:1 link to an Article (which has a Name, an AlcoholContent and some data to establish an interchangeability hierarchy) and a 1:1 link to an Unit (for the quantity to make sense).
So in a sense, Ingredient table makes a M:N relationship between Recipe and Article, and at the same time adding some additional information to each individual linked pair.
I had exactly the same problem. I had a parent / child hierarchy, and when adding and removing the child entity without saving to the database I received the "An attempt was made to remove a relationship" exception.
I discovered that this problem only arose when I set an object style property of the child to another linq-sql entity before saving. eg
1. This creates the error
RetailAccountCustomerCard racc = new RetailAccountCustomerCard();
Card addedCard = _idc.Cards.Where(c => c.CardId == card.CardId).ToList().First();
racc.Card = addedCard;
this.CurrentCustomer.RetailAccountCardsBindingList.Add(racc);
// Some code triggered by the user before saving to the db
CurrentCustomer.RetailAccountCardsBindingList.Remove(racc);
2. This doesn't create the error
RetailAccountCustomerCard racc = new RetailAccountCustomerCard();
racc.CardId = card.CardId; // note that I have set the Id property not the object
this.CurrentCustomer.RetailAccountCardsBindingList.Add(racc);
// Some code triggered by the user before saving to the db
CurrentCustomer.RetailAccountCardsBindingList.Remove(racc);
Strangely enough, the error that arises in 1. specifies the problem is to do with the relationship is on the RetailAccountCustomerId property of RetailAccountCustomerCard. IT HAS NOTHING to do with the Card object I added. It seems that simply setting any object property of the new entity triggers the problem.
NB. Example 1 works fine in terms of saving, it only causes a problem if the the new entity is deleted before saving.
I am running into a similar issue, as a workaround, I need to call DataContext.GetChanges(), then everything seems to have caught on again :)
Another problem you could have it that you are binding to columns and not entity properties, and hence the referential collections are not updated (already stated by someone else, but enforcing the fact).

LINQ to SQL disconnected updating object from different data context

Link
I'm using ASP.NET with C# and trying to use linq to sql to update a data context as exhibited on the blog linked above. I created the timestamp field in the table just as stated and am using the following method:
private void updateRecord(TableName updatedRecord)
{
context db = new context();
db.TableName.Attach(updatedRecord,true);
db.SubmitChanges();
}
My question is, are you supposed to assign the timeStamp field to anything in your updatedRecord before trying to call the Attach method on your data context?
When I run this code I get the following exception: System.Data.Linq.ChangeConflictException: Row not found or changed. I update all of the fields, including the primary key of the record that I'm updating before passing the object to this update method. During debugging the TimeStamp attribute of the object shows as null. I'm not sure if it's supposed to be that way or not.
Every book and resource I have says that this is the way to do it, but none of them go into great detail about this TimeStamp attribute.
I know this is quick and easy, so if anybody knows, please let me know.
Since you say that you created the time stamp field in the table, I wonder if, in the case where this column was added later, the column properties may not be set correctly.
You may want to check the properties on your TimeStamp column in the DBML designer. Make sure that:
AutoGenerated = true
Auto-Sync = Always
Time Stamp = True
Update Check = Never
The server data type should be rowversion NOT NULL
If it is not set to be auto generated and synced always, the row version won't be returned from the insert since you haven't changed it when the insert was done. Even though this value is generated by the database the DataContext needs to know this so that it can handle it properly.
In addition, now that you have a timestamp column, UpdateCheck should be set to Never for all of the other columns.
If you have a timestamp column, then to update a record (from a vanilla object): yes, I would expect to have to assign it. Otherwise, you lose the ability to use the timestamp for optimistic concurrency checking.
The idea is you take a copy of the timestamp when you get hold of your (disconnected) object, then when you update you can use this column to verify that nobody else has edited the row.
There are two common scenarios:
1: if you are only performing a short lived operation, get the record out of the database first - make your changes to the object, and simply SumbitChanges() [all with the same data-context]. The data-context will handle concurrency for you.
2: if you are disconnecting the object (for example passing it to a client application for a while), then use something like serialization (LINQ-to-SQL objects support DataContractSerializer (optionally; you need to enable it)). So serialize the object at the server, pass it to the client - the client makes changes to their copy and passes it back. The server deserializes it and uses Attach() and SubmitChanges(). The record in memory should still have the timestamp that it had when extracted from the database, so we can perform optimistic concurrency spanning all the time the record has been disconnected.

Categories