How to: Manage Data Concurrency in the Object Context

This topic provides an example of how to manage concurrency in an object context. The example in this topic demonstrates a scenario in which you want to make sure that saving changes made to your object on the client succeed whether or not any intermediate changes were made to the database. You will generate and handle an OptimisticConcurrencyException when updating the Status property of SalesOrderHeader object.

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.

The example in this topic is based on the Adventure Works Sales Model. To run the code in this example, you must have already added the AdventureWorks Sales Model to your project and configured your project to use the Entity Framework. To do this, complete the procedures in How to: Manually Configure an Entity Framework Project and How to: Manually Define the Model and Mapping Files.

To successfully generate an OptimisticConcurrencyException, you must modify the Status property in the conceptual mapping file.

To enable concurrency checking on the Status property

  1. Open the AdventureWorks.csdl file and locate the definition for the SalesOrderHeader entity.

  2. Find the child Status element, and add the following attribute:

    ConcurrencyMode="fixed"
    
  3. Save changes to AdventureWorks.csdl.

  4. In the following example code, set a breakpoint after the foreach loop (For Each in Visual Basic) and run the application in debug mode.

  5. When the execution breaks, use SQL Server Management Studio to execute the following Transact-SQL command against the AdventureWorks database:

    UPDATE Sales.SalesOrderHeader SET Status = 1 WHERE CreditCardApprovalCode IS NULL.
    
  6. Restart program execution.

Example

In this example, changes to the Status property of the SalesOrderHeader object cause an OptimisticConcurrencyException when the procedure above is followed.

Using context As New AdventureWorksEntities()
    Try
        ' Perform an operation with a high-level of concurrency. 
        ' Change the status of all orders without an approval code. 
        Dim orders As ObjectQuery(Of SalesOrderHeader) = context.SalesOrderHeaders.Where("it.CreditCardApprovalCode IS NULL").Top("100")

        For Each order As SalesOrderHeader In orders
            ' Reset the order status to 4 = Rejected. 
            order.Status = 4
        Next
        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

        For Each order As SalesOrderHeader In orders
            Console.WriteLine(("Order ID: " & order.SalesOrderID.ToString() & " Order status: ") + order.Status.ToString())
        Next
    Catch ex As UpdateException
        Console.WriteLine(ex.ToString())
    End Try
End Using
using (AdventureWorksEntities context =
    new AdventureWorksEntities())
{
    try
    {
        // Perform an operation with a high-level of concurrency.
        // Change the status of all orders without an approval code.
        ObjectQuery<SalesOrderHeader> orders =
            context.SalesOrderHeaders.Where(
            "it.CreditCardApprovalCode IS NULL").Top("100");

        foreach (SalesOrderHeader order in orders)
        {
            // Reset the order status to 4 = Rejected.
            order.Status = 4;
        }
        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");
        }

        foreach (SalesOrderHeader order in orders)
        {
            Console.WriteLine("Order ID: " + order.SalesOrderID.ToString()
                + " Order status: " + order.Status.ToString());
        }
    }
    catch (UpdateException ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

See Also

Concepts

Working with Objects
Saving Changes and Managing Concurrency