,

Concurrency

LINQ to SQL has built-in support for optimistic concurrency; an entity is able to be retrieved and used in more than one operation at a time. When an entity is retrieved from the database, and then updated, if changes by another party occur in the interim, they are identified and a conflict is detected.

The mechanism supporting this feature is configured using two Column attribute properties: IsVersion and UpdateCheck.

IsVersion allows you to designate an entity class member to be used for optimistic concurrency control (OCC). Before committing a change to an entity, the data context verifies that no other transaction has modified its data. If the check reveals conflicting modifications, the committing transaction rolls back and an exception is raised.


Note

IsVersion is not required for conflict detection. When not specified, however, the data context must retain copies of the entity’s member values and must compare the original values with the database values to detect conflicts, which is not terribly efficient.


LINQ to SQL supports multitier applications by allowing entities to be attached to a DataContext. Such an entity may have been retrieved using a different DataContext instance, or deserialized after being sent over the wire from another tier via a web service.

In the following example, you see that by adding a version property to the BankAccount class (presented in the previous section), you can prevent conflicting changes from being written to the database. BankAccount now contains a DateVersion property of type byte[]:

byte[] dataVersion;

[Column(
    IsVersion = true,
    IsDbGenerated = true,
    UpdateCheck = UpdateCheck.Never)]
public byte[] DataVersion
{
    get
    {
        return dataVersion;
    }
    set
    {
        Assign(ref dataVersion, value);
    }
}

The DataVersion value is materialized as a ROWVERSION field in the database (see Figure 29.12). The ROWVERSION data type causes the table field to be automatically updated whenever a row update occurs.

Image

FIGURE 29.12 By default, the version member is materialized as a ROWVERSION column.

The BankingDataContextTests class contains a test method for demonstrating conflict detection. It creates a CheckingAccount, called beforeAccount, and inserts it into the database.

It then retrieves the same account, this time in the scope of a new BankingDataContext, sets its CheckbookCode to a new value, and then updates the database.

Finally, it attaches the beforeAccount instance to a new DataContext and attempts to update the CheckbookCode to a new value. See the following excerpt:

[TestMethod]
[Tag("i2")]
[ExpectedException(typeof(ChangeConflictException))]
public void ContextShouldEnforceUpdateChecking()
{
    CheckingAccount beforeAccount;

    using (BankingDataContext context = databaseUtility.CreateContext())
    {
        beforeAccount = new CheckingAccount
                            {
                                Balance = 100,
                                CheckbookCode = "11111"
                            };
        context.BankAccounts.InsertOnSubmit(beforeAccount);
        context.SubmitChanges();
    }

    using (BankingDataContext context = databaseUtility.CreateContext())
    {
        CheckingAccount afterAccount
              = (CheckingAccount)context.BankAccounts.Where(
                            x => x.Id == beforeAccount.Id).First();
        afterAccount.CheckbookCode = "22222";
        context.SubmitChanges();
    }

    using (BankingDataContext context = databaseUtility.CreateContext())
    {
        context.BankAccounts.Attach(beforeAccount);
        beforeAccount.CheckbookCode = "33333";
        context.SubmitChanges();
    }
}

When context.SubmitChanges is called, a conflict is detected because the afterAccount object, representing the same account, was updated in the database in the interim. This raises a ChangeConflictException (see Figure 29.13), which is defined as an expected exception, using the ExpectedException test attribute shown in the previous excerpt.

Image

FIGURE 29.13 A ChangeConflictException is raised when a conflict is detected.

Providing a dedicated version property within your entity classes is good practice when enabling conflict detection. When not used, the data context relies solely on the Column attribute’s UpdateCheck property for inferring conflict detection behavior.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.31.206