Database Reference
In-Depth Information
call SaveChanges() , Entity Framework generates an update statement with a where clause that includes both the
ProductId and the TimeStamp values we have for the product. The value for this TimeStamp is the one retrieved
when we read the product from the database before the out-of-band update. Because the out-of-band update caused
the TimeStamp to change, the value for the TimeStamp column in the database is different from the value of the
TimeStamp property on the product entity in the database context. The update statement will fail because no row is
found in the table matching both the ProductId and the TimeStamp values. Entity Framework will respond by rolling
back the entire transaction and throwing a DbUpdateConcurrencyException .
In responding to the exception, the code in Listing 14-1 printed a message and continued. This is probably not
how you would handle a concurrency violation in a real application. One way to handle this exception is to refresh the
entity with the current value of the concurrency column from the database. With the correct value for the concurrency
column, a subsequent SaveChanges() will likely succeed. Of course, it might not for the same reason that it failed the
first time, and you need to be prepared for this as well.
The DbUpdateConcurrencyException object has an Entries collection property, which contains a DbEntityEntry
instance for each entity that fails to update. The DbEntityEntry class defines a Reload() method that will cause the
entry to be updated with the values from the database (database wins), and all changes made to the entry in the
database context are lost.
It is possible, however, to overwrite the entry's OriginalValues property such that SaveChanges() can be called on
the database context without a concurrency violation, as shown in Listing 14-2.
Listing 14-2. Resolving a Concurrency Conflict in a Client Wins Scenario
bool saveChangesFailed;
do
{
saveChangesFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveChangesFailed = true;
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveChangesFailed);
In addition to the two aforementioned scenarios, it is possible to write custom code to update a conflicting entity
with data from both the database and the client, or to allow user intervention to resolve data conflicts.
14-2. Managing Concurrency When Using Stored Procedures
Problem
You want to use optimistic concurrency when using stored procedures for the insert, update, and delete actions.
Solution
Let's suppose that we have a table like the one shown in Figure 14-3 and the entity shown in Listing 14-3, which is
mapped to the table.
 
Search WWH ::




Custom Search