Saving Changes and Managing Concurrency (Entity Framework)

The Entity Framework implements an optimistic concurrency model. This means that locks are not held on data in the data source. By default, however, Object Services saves object changes to the database without checking for concurrency. For properties that might experience a high degree of concurrency, we recommend that the entity property be defined 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, Object Services 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 (Entity Framework). 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. For more information see Stored Procedure Support (Entity Framework).

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 Object Services 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.

Object Services tracks changes that have been made to objects in the cache. When the SaveChanges method is called, Object Services 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 = context.SaveChanges()
    Console.WriteLine("No conflicts. " _
                      & num.ToString() & " updates saved.")
Catch ex 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 (Entity Framework).

Object Services also honors transactions that were defined by using the System.Transactions namespace. For more information, see Managing Transactions in Object Services (Entity Framework).

See Also

Concepts

Adding, Modifying, and Deleting Objects (Entity Framework)

Other Resources

Working with Objects (Entity Framework)