Skip to main content

LINQ To SQL Samples - Simultaneous Changes


Optimistic Concurrency - 1

This and the following sample demonstrate optimistic concurrency. In this sample, the other user makes and commits his update to Product 1 before you read the data so no conflict occurs.

Public Sub LinqToSqlSimultaneous01()
    Console.WriteLine("OTHER USER: ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~")

    ' Open a second connection to the database to simulate another user
    ' who is going to make changes to the Products table                
    Dim otherUser_db As NorthwindDataContext = New NorthwindDataContext()
    otherUser_db.Log = db.Log

    Dim otherUser_product = (From p In otherUser_db.Products _
        Where p.ProductID = 1 _
        Select p).First()
    otherUser_product.UnitPrice = 999.99
    otherUser_db.SubmitChanges()

    Console.WriteLine("~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~")


    Console.WriteLine()
    Console.WriteLine("YOU:  ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~")

    Dim product = (From p In db.Products _
        Where p.ProductID = 1 _
        Select p).First()
    product.UnitPrice = 777.77

    Dim conflict As Boolean = False
    Try
        db.SubmitChanges()
    Catch x As DBConcurrencyException
        conflict = True
    End Try

    Console.WriteLine()
    If (conflict) Then
        Console.WriteLine("* * * OPTIMISTIC CONCURRENCY EXCEPTION * * *")
        Console.WriteLine("Another user has changed Product 1 since it was first requested.")
        Console.WriteLine("Backing out changes.")
    Else
        Console.WriteLine("* * * COMMIT SUCCESSFUL * * *")
        Console.WriteLine("Changes to Product 1 saved.")
    End If

    Console.WriteLine("~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~")



    Cleanup63()  ' Restore previous database state
End Sub

Result:
OTHER USER: ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

YOU: ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

* * * COMMIT SUCCESSFUL * * *
Changes to Product 1 saved.
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~


Transactions - Implicit

This sample demonstrates the implicit transaction created by SubmitChanges. The update to prod2's UnitsInStock field makes its value negative, which violates a check constraint on the server. This causes the transaction that is updating both Products to fail, which rolls back all changes.

Public Sub LinqToSqlSimultaenous03()
    Console.WriteLine("*** BEFORE ***")
    ObjectDumper.Write(From p In db.Products Where p.ProductID = 4 Select p)
    ObjectDumper.Write(From p In db.Products Where p.ProductID = 5)

    Console.WriteLine()
    Console.WriteLine("*** UPDATE WITH IMPLICIT TRANSACTION ***")
    Try
        Dim prod1 As Product = (From p In db.Products _
            Where p.ProductID = 4 _
            Select p).First()
        Dim prod2 As Product = (From p In db.Products _
            Where p.ProductID = 5 _
            Select p).First()
        prod1.UnitsInStock = New Short?(prod1.UnitsInStock - 3)
        prod2.UnitsInStock = New Short?(prod2.UnitsInStock - 5)    ' ERROR: this will make the units in stock negative
        ' db.SubmitChanges implicitly uses a transaction so that
        ' either both updates are accepted or both are rejected
        db.SubmitChanges()
    Catch e As Exception
        Console.WriteLine(e.Message)
    End Try

    Console.WriteLine()
    Console.WriteLine("*** AFTER ***")
    clearDBCache()
    ObjectDumper.Write(From p In db.Products _
        Where p.ProductID = 4 _
        Select p)
    ObjectDumper.Write(From p In db.Products _
        Where p.ProductID = 5 _
        Select p)

    Cleanup95()  ' Restore previous database state
End Sub

Result:
*** BEFORE ***
ProductID=4     ProductName=Chef Anton's Cajun Seasoning        SupplierID=2    CategoryID=2    QuantityPerUnit=48 - 6 oz jars  UnitPrice=22.0000       UnitsInStock=53         UnitsOnOrder=0  ReorderLevel=0  Discontinued=False      Order_Details=...       Category={ }    Supplier={ }
ProductID=5     ProductName=Chef Anton's Gumbo Mix      SupplierID=2    CategoryID=2    QuantityPerUnit=36 boxes        UnitPrice=22.3500       UnitsInStock=0  UnitsOnOrder=0  ReorderLevel=0  Discontinued=True       Order_Details=...       Category={ }    Supplier={ }

*** UPDATE WITH IMPLICIT TRANSACTION ***
The UPDATE statement conflicted with the CHECK constraint "CK_UnitsInStock". The conflict occurred in database "CE559A7E617443375E2DBA1456355C1C_L STUDIO 2008\PROJECTS\SAMPLEQUERIESTOCONTENT\SAMPLE QUERIES\BIN\DEBUG\SAMPLEDATA\NORTHWIND.MDF", table "dbo.Products", column 'UnitsInStock'.
The statement has been terminated.

*** AFTER ***
ProductID=4     ProductName=Chef Anton's Cajun Seasoning        SupplierID=2    CategoryID=2    QuantityPerUnit=48 - 6 oz jars  UnitPrice=22.0000       UnitsInStock=53         UnitsOnOrder=0  ReorderLevel=0  Discontinued=False      Order_Details=...       Category={ }    Supplier={ }
ProductID=5     ProductName=Chef Anton's Gumbo Mix      SupplierID=2    CategoryID=2    QuantityPerUnit=36 boxes        UnitPrice=22.3500       UnitsInStock=0  UnitsOnOrder=0  ReorderLevel=0  Discontinued=True       Order_Details=...       Category={ }    Supplier={ }


Transactions - Explicit

This sample demonstrates using an explicit transaction. This provides more protection by including the reading of the data in the transaction to help prevent optimistic concurrency exceptions. As in the previous query, the update to prod2's UnitsInStock field makes the value negative, which violates a check constraint within the database. This causes the transaction that is updating both Products to fail, which rolls back all changes.

Public Sub LinqToSqlSimultaneous04()
    Console.WriteLine("*** BEFORE ***")
    ObjectDumper.Write(From p In db.Products _
        Where p.ProductID = 4 _
        Select p)
    ObjectDumper.Write(From p In db.Products _
        Where p.ProductID = 5 _
        Select p)


    Console.WriteLine()
    Console.WriteLine("*** UPDATE WITH EXPLICIT TRANSACTION ***")
    ' Explicit use of TransactionScope ensures that
    ' the data will not change in the database between
    ' read and write

    Using ts As TransactionScope = New TransactionScope()
        Try
            Dim prod1 As Product = (From p In db.Products _
                Where p.ProductID = 4 _
                Select p).First()
            Dim prod2 As Product = (From p In db.Products _
                Where p.ProductID = 5 _
                Select p).First()
            prod1.UnitsInStock = New Short?(prod1.UnitsInStock - 3)
            prod2.UnitsInStock = New Short?(prod2.UnitsInStock - 5)    ' ERROR: this will make the units in stock negative
            db.SubmitChanges()
        Catch e As Exception
            Console.WriteLine(e.Message)
        End Try
    End Using

    Console.WriteLine()
    Console.WriteLine("*** AFTER ***")
    clearDBCache()
    ObjectDumper.Write(From p In db.Products _
        Where p.ProductID = 4 _
        Select p)
    ObjectDumper.Write(From p In db.Products _
        Where p.ProductID = 5 _
        Select p)

    Cleanup66()  ' Restore previous database state
End Sub

Result:
*** BEFORE ***
ProductID=4     ProductName=Chef Anton's Cajun Seasoning        SupplierID=2    CategoryID=2    QuantityPerUnit=48 - 6 oz jars  UnitPrice=22.0000       UnitsInStock=53         UnitsOnOrder=0  ReorderLevel=0  Discontinued=False      Order_Details=...       Category={ }    Supplier={ }
ProductID=5     ProductName=Chef Anton's Gumbo Mix      SupplierID=2    CategoryID=2    QuantityPerUnit=36 boxes        UnitPrice=22.3500       UnitsInStock=0  UnitsOnOrder=0  ReorderLevel=0  Discontinued=True       Order_Details=...       Category={ }    Supplier={ }

*** UPDATE WITH EXPLICIT TRANSACTION ***
The UPDATE statement conflicted with the CHECK constraint "CK_UnitsInStock". The conflict occurred in database "CE559A7E617443375E2DBA1456355C1C_L STUDIO 2008\PROJECTS\SAMPLEQUERIESTOCONTENT\SAMPLE QUERIES\BIN\DEBUG\SAMPLEDATA\NORTHWIND.MDF", table "dbo.Products", column 'UnitsInStock'.
The statement has been terminated.

*** AFTER ***
ProductID=4     ProductName=Chef Anton's Cajun Seasoning        SupplierID=2    CategoryID=2    QuantityPerUnit=48 - 6 oz jars  UnitPrice=22.0000       UnitsInStock=53         UnitsOnOrder=0  ReorderLevel=0  Discontinued=False      Order_Details=...       Category={ }    Supplier={ }
ProductID=5     ProductName=Chef Anton's Gumbo Mix      SupplierID=2    CategoryID=2    QuantityPerUnit=36 boxes        UnitPrice=22.3500       UnitsInStock=0  UnitsOnOrder=0  ReorderLevel=0  Discontinued=True       Order_Details=...       Category={ }    Supplier={ }