Quick Objects Documentation Akal Tech Logo
Concurrency Management During an Update or Delete

Glossary Item Box

Business Logic Framework - Tutorial 11:

Our eleventh tutorial shows you how to update a single record to the database and prevent any accidental overwriting of underlying data that might have been changed.

Problem:

In today's environment there are more and more applications that work in disconnected mode, meaning that they allow the user to manipulate data while the connection to the database is no longer open. This paradigm creates a very fundamental problem in multi user environment i.e. the ability for more than one person to be modifying the same data while they are disconnected. Hence one user may change data and update the database while the other user may still be working with the old data. In this situation when the second user issues an update command on the same record that was already updated by the first user be default the update command will overwrite first user's changes. This may be acceptable in some application scenarios but at the same time lot of scenarios require that a user may only update the data if no one else has modified the underlying record and may not overwrite someone else's changes.

Solution:

Industry standard practice is to use the original values that were retrieved and construct a where clause with those values and use it in the UPDATE or DELETE statement. This can require you to retain the old values while the user modifies them and result in lot of extra code and effort to ensure this technique is applied to each update or delete statement.

QuickObjects Business Logic Framework on the other hand provides a very easy solution to implement this technique. All that needs to be done is to set a property called ConcurrencyMode to ConcurrencyModes.DetectChanges. This ensures that any original values that were loaded are used to check the underlying row hasn't changed during an update or delete command.

Here is the example code that performs concurrency check for Update and Delete:

1 private Customers customer = new Customers();  
2 private void Button_LoadCustomer_Click(object sender, EventArgs e)  
3 {  
4     if (this.TextBox_CustomerID.Text.Trim().Length > 0)  
5     {  
6         // The following line will indicate to the customer object that concurrency check should   
7         // be performed during an update or delete operation. By default this check is not performed   
8         // and the row is simply updated or deleted. By enabling this feature of the business object   
9         // you ensure that if the underlying data in the database has not changed after the original   
10         // record was fetched. If the underlying record has changed then the Update or Delete operation will   
11         // not update or delete the record respectively. This mode does not provide any feedback in form of   
12         // throwing an exception or providing an error if it fails to update or delete the underlying database   
13         // record. Hence you can use the AffectedRecords property or the return  
14         // value of the Delete method to ensure weather the delete was successful or not.  
15         customer.ConcurrencyMode = Akal.QuickObjects.ObjectBase.ConcurrencyModes.DetectChanges;  
16           
17         // the following line displays a very useful method on each field type object  
18         // Parse method takes any string value and converts it to the correct type  
19         // and assigns the converted value into the Value property.  
20         customer.CustomerID.Parse(this.TextBox_CustomerID.Text);  
21  
22         // Since we do not want to modify the Photo we don't have to load it.  
23         customer.Photo.Visible = false;  
24  
25         // Load method expects that the primary key field(s) of the business object  
26         // have already been assigned values. Once the value is assigned   
27         // to the primary key fields, calling the Load method will load all field values  
28         // into the business object.  
29         if (customer.Load())  
30         {  
31  
32             // Since we have already called the Load method and it was successful  
33             // we can now access the values of all the fields.  
34             this.TextBox_FirstName.Text = customer.FirstName.Value;  
35             this.TextBox_LastName.Text = customer.LastName.Value;  
36               
37             // Just for the visual reference sake we will now display the loaded values.  
38             // These values will not change on Update but will be used by the concurrency manager  
39             // to perform a check and ensure that the underlying row is still the same as the   
40             // one we loaded into the business object.  
41             this.TextBox_OFirstName.Text = customer.FirstName.OriginalValue.Value.ToString();  
42             this.TextBox_OLastName.Text = customer.LastName.OriginalValue.Value.ToString();  
43         }  
44         else 
45         {  
46             this.ResetTextBoxes();  
47         }  
48     }  
49     else 
50     {  
51         this.ResetTextBoxes();  
52     }  
53 }  
54  
55 private void ResetTextBoxes()  
56 {  
57     this.TextBox_CustomerID.Enabled = true;  
58     this.Button_Save.Enabled = false;  
59     this.TextBox_FirstName.Text = "";  
60     this.TextBox_LastName.Text = "";  
61 }  
62  
63 private void Button_Save_Click(object sender, EventArgs e)  
64 {  
65     if (this.TextBox_FirstName.Text.Trim().Length > 0  
66         && this.TextBox_LastName.Text.Trim().Length > 0)  
67     {  
68         // Setting the ObjectMode property of an object allow the object to   
69         // know what it is supposed to do with the data you are supplying it with.  
70         // As shown in the example below, once the ObjectMode is set to Save  
71         // each value that you assign to this business object's (customer) properties  
72         // will be used to update the underlying data base record for this object.  
73         customer.ObjectMode = Akal.QuickObjects.ObjectBase.ObjectModes.Save;  
74  
75         // Assigning the value to the Primary Key field(s) is required for the object   
76         // to be able to update.  
77         customer.CustomerID.Parse(this.TextBox_CustomerID.Text);  
78         customer.FirstName.Value = this.TextBox_FirstName.Text;  
79         customer.LastName.Value = this.TextBox_LastName.Text;  
80         // Calling Update method is all you need after assigning the values that need to be updated.  
81         // since the concurrency mode is already set to DetectChanges calling the Update method  
82         // will automatically utilize the concurrency manager to detect underlying row changes before  
83         // performing the update.  
84  
85         if (customer.Update())  
86         {  
87             MessageBox.Show("Customer Saved!");  
88         }  
89         else 
90         {  
91             if (customer.ErrorString == null)  
92             {  
93                 MessageBox.Show("The underlying row was modified by a different user or a process and your changes were not saved.\nPlease reload the object and save your changes again.");  
94             }  
95             else 
96             {  
97                 MessageBox.Show(customer.ErrorString);  
98             }  
99         }  
100     }  
101     else 
102     {  
103         MessageBox.Show("First Name and Last Name must be specified");  
104     }  
105 }  
106  
107 private void Button_Delete_Click(object sender, EventArgs e)  
108 {  
109     // Since the ConcurrencyMode is already set to DetectChanges calling the Delete  
110     // method will only work if the underlying record in the database still has the same values  
111     // as the ones that were fetched earlier by the business object in the Load method call.  
112     if (customer.Delete())  
113     {  
114         MessageBox.Show("Customer Deleted!");  
115         this.ResetTextBoxes();  
116     }  
117     else 
118     {  
119         if (customer.ErrorString == null)  
120         {  
121             MessageBox.Show("The underlying row was modified by a different user or a process and the business object did not delete your record.\nPlease reload the object and then try delete again.");  
122         }  
123         else 
124         {  
125             MessageBox.Show(customer.ErrorString);  
126         }  
127     }  
128 }  
129  
1 Private customer As Customers = New Customers()  
2 Private Sub Button_LoadCustomer_Click(ByVal sender As ObjectByVal e As EventArgs) Handles Button_LoadCustomer.Click  
3     If Me.TextBox_CustomerID.Text.Trim().Length > 0 Then 
4         ' The following line will indicate to the customer object that concurrency check should   
5         ' be performed during an update or delete operation. By default this check is not performed   
6         ' and the row is simply updated or deleted. By enabling this feature of the business object   
7         ' you ensure that if the underlying data in the database has not changed after the original   
8         ' record was fetched. If the underlying record has changed then the Update or Delete operation will   
9         ' not update or delete the record respectively. This mode does not provide any feedback in form of   
10         ' throwing an exception or providing an error if it fails to update or delete the underlying database   
11         ' record. Hence you can use the AffectedRecords property or the return  
12         ' value of the Delete method to ensure weather the delete was successful or not.  
13         customer.ConcurrencyMode = Akal.QuickObjects.ObjectBase.ConcurrencyModes.DetectChanges  
14  
15         ' the following line displays a very useful method on each field type object  
16         ' Parse method takes any string value and converts it to the correct type  
17         ' and assigns the converted value into the Value property.  
18         customer.CustomerID.Parse(Me.TextBox_CustomerID.Text)  
19  
20         ' Since we do not want to modify the Photo we don't have to load it.  
21         customer.Photo.Visible = False 
22  
23         ' Load method expects that the primary key field(s) of the business object  
24         ' have already been assigned values. Once the value is assigned   
25         ' to the primary key fields, calling the Load method will load all field values  
26         ' into the business object.  
27         If customer.Load() Then 
28  
29             ' Since we have already called the Load method and it was successful  
30             ' we can now access the values of all the fields.  
31             Me.TextBox_FirstName.Text = customer.FirstName.Value  
32             Me.TextBox_LastName.Text = customer.LastName.Value  
33  
34             ' Just for the visual reference sake we will now display the loaded values.  
35             ' These values will not change on Update but will be used by the concurrency manager  
36             ' to perform a check and ensure that the underlying row is still the same as the   
37             ' one we loaded into the business object.  
38             Me.TextBox_OFirstName.Text = customer.FirstName.OriginalValue.Value.ToString()  
39             Me.TextBox_OLastName.Text = customer.LastName.OriginalValue.Value.ToString()  
40         Else 
41             Me.ResetTextBoxes()  
42         End If 
43     Else 
44         Me.ResetTextBoxes()  
45     End If 
46 End Sub 
47  
48 Private Sub ResetTextBoxes()  
49     Me.TextBox_CustomerID.Enabled = True 
50     Me.Button_Save.Enabled = False 
51     Me.TextBox_FirstName.Text = "" 
52     Me.TextBox_LastName.Text = "" 
53 End Sub 
54  
55 Private Sub Button_Save_Click(ByVal sender As ObjectByVal e As EventArgs) Handles Button_Save.Click  
56     If (Me.TextBox_FirstName.Text.Trim().Length > 0 And Me.TextBox_LastName.Text.Trim().Length > 0) Then 
57         ' Setting the ObjectMode property of an object allow the object to   
58         ' know what it is supposed to do with the data you are supplying it with.  
59         ' As shown in the example below, once the ObjectMode is set to Save  
60         ' each value that you assign to this business object's (customer) properties  
61         ' will be used to update the underlying data base record for this object.  
62         customer.ObjectMode = Akal.QuickObjects.ObjectBase.ObjectModes.Save  
63  
64         ' Assigning the value to the Primary Key field(s) is required for the object   
65         ' to be able to update.  
66         customer.CustomerID.Parse(Me.TextBox_CustomerID.Text)  
67         customer.FirstName.Value = Me.TextBox_FirstName.Text  
68         customer.LastName.Value = Me.TextBox_LastName.Text  
69         ' Calling Update method is all you need after assigning the values that need to be updated.  
70         ' since the concurrency mode is already set to DetectChanges calling the Update method  
71         ' will automatically utilize the concurrency manager to detect underlying row changes before  
72         ' performing the update.  
73         If customer.Update() Then 
74             MessageBox.Show("Customer Saved!")  
75         Else 
76             If customer.ErrorString Is Nothing Then 
77                 MessageBox.Show("The underlying row was modified by a different user or a process and your changes were not saved.\nPlease reload the object and save your changes again.")  
78             Else 
79                 MessageBox.Show(customer.ErrorString)  
80             End If 
81         End If 
82     Else 
83         MessageBox.Show("First Name and Last Name must be specified")  
84     End If 
85 End Sub 
86  
87 Private Sub Button_Delete_Click(ByVal sender As ObjectByVal e As EventArgs) Handles Button_Delete.Click  
88     ' Since the ConcurrencyMode is already set to DetectChanges calling the Delete  
89     ' method will only work if the underlying record in the database still has the same values  
90     ' as the ones that were fetched earlier by the business object in the Load method call.  
91     If customer.Delete() Then 
92         MessageBox.Show("Customer Deleted!")  
93         Me.ResetTextBoxes()  
94     Else 
95         If customer.ErrorString Is Nothing Then 
96             MessageBox.Show("The underlying row was modified by a different user or a process and the business object did not delete your record.\nPlease reload the object and then try delete again.")  
97         Else 
98             MessageBox.Show(customer.ErrorString)  
99         End If 
100     End If 
101 End Sub 
102  

 

Enter a value for Customer ID


 

Now Click on the "Load Customer" button

 

 

You will now see that the Customer Details have been loaded and the First and Last Names of the customer will appear in the text boxes.

Now modify these values and click on the Save button. Since the underlying row in the database hasn't changed the Update will be successful.

 

Now Click Save button again, and since the underlying row is different from the one originally loaded in the business object, the Update operation will not work. 

 

Now lets enter a different Customer ID and click on the Load Customer button.

 

Once the customer details are loaded we can click on the Delete button.

To simulate a more real life scenario, simply update the underlying customer row in the database before clicking on the Delete button. You will see that the business object will not delete the customer row since it has already changed in the database.