Saving Changes and Managing Concurrency

By default, the Entity Framework implements an optimistic concurrency model. This means that locks are not held on data in the data source between when the data is queried and the data is updated. The Entity Framework saves object changes to the database without checking for concurrency. For entities that might experience a high degree of concurrency, we recommend that the entity define a property in the conceptual layer with an attribute of ConcurrencyMode="fixed", as shown in the following example: 

<Property Name="Status" Type="Byte" Nullable="false" ConcurrencyMode="Fixed" />

When this attribute is used, the Entity Framework checks for changes in the database before saving changes to the database. Any conflicting changes will cause an OptimisticConcurrencyException. For more information, see How to: Manage Data Concurrency in the Object Context. An OptimisticConcurrencyException can also occur when you define an Entity Data Model that uses stored procedures to make updates to the data source. In this case, the exception is raised when the stored procedure that is used to perform updates reports that zero rows were updated.

Note

The way you handle concurrency exceptions depends on the business rules required by your application. Mike Taulty discusses some considerations and possible ways to handle concurrency conflicts in the following blog: On Entity Framework, Concurrency.

When making updates in such high concurrency scenarios, we recommend that you call Refresh frequently. When you call Refresh, the RefreshMode controls how changes are propagated. The StoreWins option will cause the Entity Framework to overwrite all data in the object cache with corresponding values from the database. Conversely, the ClientWins option replaces the original values in the cache with the latest values from the data source. This ensures that all changed data in the object cache can be successfully saved back to the data source, by eliminating conflicts between changes that were made to data in the cache and changes that you made to the same data in the data source. 

Call the Refresh method after calling the SaveChanges method if updates to the data source may modify the data that belongs to other objects in the object context. For example, in the AdventureWorks Sales model when a new SalesOrderDetail is added, triggers update the SubTotal column to reflect the subtotal with the new item. In this case, call the Refresh method and pass the SalesOrderHeader object for the order. This ensures that trigger-generated values are sent back to the SalesOrderHeader object in the object context.

the Entity Framework tracks changes that have been made to objects in the cache. When the SaveChanges method is called, the Entity Framework tries to merge changes back to the data source. SaveChanges can fail with an OptimisticConcurrencyException when data changes in the object cache conflict with changes that were made in the data source after objects were added to or refreshed in the cache. This causes the whole transaction to be rolled-back. When an OptimisticConcurrencyException occurs, you should handle it by calling Refresh, and specifying whether the conflict should be resolved by preserving data in the object data (ClientWins) or by updating the object cache with the data from the data source (StoreWins), as in the following example:

Try
    ' Try to save changes, which may cause a conflict. 
    Dim num As Integer = context.SaveChanges()
    Console.WriteLine("No conflicts. " & num.ToString() & " updates saved.")
Catch generatedExceptionName As OptimisticConcurrencyException
    ' Resolve the concurrency conflict by refreshing the 
    ' object context before re-saving changes. 
    context.Refresh(RefreshMode.ClientWins, orders)

    ' Save changes. 
    context.SaveChanges()
    Console.WriteLine("OptimisticConcurrencyException handled and changes saved")
End Try
try
{
    // Try to save changes, which may cause a conflict.
    int num = context.SaveChanges();
    Console.WriteLine("No conflicts. " +
        num.ToString() + " updates saved.");
}
catch (OptimisticConcurrencyException)
{
    // Resolve the concurrency conflict by refreshing the 
    // object context before re-saving changes. 
    context.Refresh(RefreshMode.ClientWins, orders);

    // Save changes.
    context.SaveChanges();
    Console.WriteLine("OptimisticConcurrencyException "
    + "handled and changes saved");
}

SaveChanges can generate an UpdateException when an object added to the ObjectContext cannot be successfully created in the data source. This can happen if a row with the foreign key specified by the relationship already exists. When this occurs, you cannot use Refresh to update the added object in the object context. Instead, reload the object with a value of OverwriteChanges for MergeOption.

For more information about managing the object context, see How to: Manage Data Concurrency in the Object Context.

You may choose to use transactions as an alternative to optimistic concurrency. For more information, see Managing Connections and Transactions.

In This Section

How to: Manage Data Concurrency in the Object Context

See Also

Concepts

Working with Objects
Creating, Adding, Modifying, and Deleting Objects

Other Resources

Handling Concurrency with the Entity Framework in an ASP.NET MVC Application
Handling Concurrency with the Entity Framework in an ASP.NET Web Forms Application