Tutorial: MSSQL- Windows Form Development
Multi-User Application With Custom Record Lockout #4
[Edit Mode, Normal Mode, Record-Lock Database, Record-Lockout Timing,
Insert Lock Request, Complete Lock Request]
Dr. Thomas E. Hicks
Computer Science Department
Trinity University
The purpose of this series of tutorials is to show how to
configure a Windows Application Form, using Visual Studio, to process a
multi-form, multi-user network application. This tutorial will briefly review
many of the concepts demonstrated in some of the other tutorials.
Prerequisite Condition: each of our major database tables must have a unique,
auto-incremented, integer field called ID.
This portion of the tutorial will (1) construct the RecordLock datatabase, (2) create numerous timing utilities that shall help us evaluate when a lock has timed out, (3) create a number of diagnostic utilities that shall help us view the database and record-lock queue, (4) create record-lock queue request utilities, and (5) create record-lock queue construction utilities.
This tutorial is fourth in a series of
Windows Form Development Multi-User Application With Custom Record Lockout #1 [Database Configuration, Database Authentication, Generic Database Utilities, Etc.]. The topics in part 1 are as follows:Part I-A:
Part I-B:
Part I-C:
Part I-D:
Part I-E:
Part I-F:
Part I-G:
Part I-H:
This tutorial is fourth in a series of
Windows Form Development Multi-User Application With Custom Record Lockout #2 [Query Set Views & Navigation Buttons & Filters & Order By]. The topics in part 2 are as follows:Part II-A:
Part II-B:
Part II-C:
Part II-D:
Part II-E:
Part II-F:
Part II-G:
Part II-H:
Part II-I:
Part II-J:
For You To DoThis tutorial is fourth in a series of
Windows Form Development Multi-User Application With Custom Record Lockout #3 [Login, RecoordLock DB, Authentication, Configure Dynamic Data Combo, Password Fields, CheckBoxes, & Tab Control]. The topics in part 3 are as follows:Part III-A:
Part III-B
Part III-C
Part III-D:
Part III-F:
Part III-G:
Part III-H: Final Testing
Part III-I: Temporary Main.cs Change For Rapid Development
Part III-J:
Part IV-A: Edit Mode & Normal Mode
Part IV-B: Record Lock Timing Support Functions
Part IV-C :Update Class & Functions
Part IV-D :Advanced Timing Functions & Lock Expiration
Part IV-E :Record Lock Request Support Functions
Part IV-F: For You To Do
If You Have Difficulty With This Tutorial, You Might Find These Help
Prepare You For This Tutorial
I Recommend That My Students Complete The Tutorials In This Order!
Tutorial: MSSQL-Forms - Windows Application & Basic Form Properties
Tutorial: MSSQL-Forms - Windows Application - Labels & Textboxes
Tutorial: MSSQL-Forms - Windows Application - Buttons
Tutorial: MSSQL-Connection\MSSQL-Connections
Tutorial: MSSQL-Forms - Windows Application - Menu - Multiple Forms
Tutorial: MSSQL-Visual Studio MenuStrip Application With Multiple Windows
1] Copy folder Library-Net-Multi-User-3 to folder Library-Net-Multi-User-4. I always recommend making a backup copy about ever hour or so.
1] Change the UserClassDiagnosticLevel to 12. (See Below!)

2] Open the User.cs form. Replace function TestingMaster with a pasted copy of the block of code below!
//================================================================================//
// TestingMaster //
//================================================================================//
// Purpose: Testing Master which evokes TestModule1, TestModule2, ... //
// //
// Written By : Dr. Thomas E. Hicks Environment : .NET 2005/8 //
// Date : xx/xx/xxxx Language : C# //
//================================================================================//
public void TestingMaster()
{
int UserClassDiagnosticLevel = 36;
if (UserClassDiagnosticLevel == 0)
TestAll();
if (UserClassDiagnosticLevel == 1)
TestModule1();
if (UserClassDiagnosticLevel == 2)
TestModule2();
if (UserClassDiagnosticLevel == 3)
TestModule3();
if (UserClassDiagnosticLevel == 4)
TestModule4();
if (UserClassDiagnosticLevel == 5)
TestModule5();
if (UserClassDiagnosticLevel == 6)
TestModule6();
if (UserClassDiagnosticLevel == 7)
TestModule7();
if (UserClassDiagnosticLevel == 8)
TestModule8();
if (UserClassDiagnosticLevel == 9)
TestModule9();
if (UserClassDiagnosticLevel == 10)
TestModule10();
if (UserClassDiagnosticLevel == 11)
TestModule11();
if (UserClassDiagnosticLevel == 12)
TestModule12();
if (UserClassDiagnosticLevel == 13)
TestModule13();
if (UserClassDiagnosticLevel == 14)
TestModule14();
if (UserClassDiagnosticLevel == 15)
TestModule15();
if (UserClassDiagnosticLevel == 16)
TestModule16();
if (UserClassDiagnosticLevel == 17)
TestModule17();
if (UserClassDiagnosticLevel == 18)
TestModule18();
if (UserClassDiagnosticLevel == 19)
TestModule19();
if (UserClassDiagnosticLevel == 20)
TestModule20();
if (UserClassDiagnosticLevel == 21)
TestModule21();
if (UserClassDiagnosticLevel == 22)
TestModule22();
if (UserClassDiagnosticLevel == 23)
TestModule23();
if (UserClassDiagnosticLevel == 24)
TestModule24();
if (UserClassDiagnosticLevel == 25)
TestModule25();
if (UserClassDiagnosticLevel == 26)
TestModule26();
if (UserClassDiagnosticLevel == 27)
TestModule27();
if (UserClassDiagnosticLevel == 28)
TestModule28();
if (UserClassDiagnosticLevel == 29)
TestModule29();
if (UserClassDiagnosticLevel == 30)
TestModule30();
if (UserClassDiagnosticLevel == 31)
TestModule31();
if (UserClassDiagnosticLevel == 32)
TestModule32();
if (UserClassDiagnosticLevel == 33)
TestModule33();
if (UserClassDiagnosticLevel == 34)
TestModule34();
if (UserClassDiagnosticLevel == 35)
TestModule35();
if (UserClassDiagnosticLevel == 36)
TestModule36();
}
2] Add function TestAll.
//================================================================================//
// TestingAll //
//================================================================================//
// Purpose: Test all of the functions: TestModule11, TestModule12, ... //
// //
// Written By : Dr. Thomas E. Hicks Environment : .NET 2005/8 //
// Date : xx/xx/xxxx Language : C# //
//================================================================================//
public void TestAll()
{
TestModule11();
TestModule12();
TestModule13();
TestModule14();
TestModule15();
TestModule16();
TestModule17();
TestModule18();
TestModule19();
TestModule20();
TestModule21();
TestModule22();
TestModule23();
TestModule24();
TestModule25();
TestModule26();
TestModule27();
TestModule28();
TestModule29();
TestModule30();
TestModule31();
TestModule32();
TestModule33();
TestModule34();
TestModule35();
TestModule36();
}
1] Add the following Test function shells to Users.cs.
//================================================================================//
// TestModule24 //
//================================================================================//
public void TestModule24()
{
MessageBox.Show("TestModule24");
}
//================================================================================//
// TestModule25 //
//================================================================================//
public void TestModule25()
{
MessageBox.Show("TestModule25");
}
//================================================================================//
// TestModule26 //
//================================================================================//
public void TestModule26()
{
MessageBox.Show("TestModule26");
}
//================================================================================//
// TestModule27 //
//================================================================================//
public void TestModule25()
{
MessageBox.Show("TestModule27");
}
//================================================================================//
// TestModule28 //
//================================================================================//
public void TestModule28()
{
MessageBox.Show("TestModule28");
}
//================================================================================//
// TestModule29 //
//================================================================================//
public void TestModule29()
{
MessageBox.Show("TestModule29");
}
//================================================================================//
// TestModule30 //
//================================================================================//
public void TestModule30()
{
MessageBox.Show("TestModule30");
}
//================================================================================//
// TestModule31 //
//================================================================================//
public void TestModule31()
{
MessageBox.Show("TestModule31");
}
//================================================================================//
// TestModule32 //
//================================================================================//
public void TestModule32()
{
MessageBox.Show("TestModule32");
}
//================================================================================//
// TestModule33 //
//================================================================================//
public void TestModule33()
{
MessageBox.Show("TestModule33");
}
//================================================================================//
// TestModule34 //
//================================================================================//
public void TestModule34()
{
MessageBox.Show("TestModule34");
}
//================================================================================//
// TestModule35 //
//================================================================================//
public void TestModule35()
{
MessageBox.Show("TestModule35");
}
//================================================================================//
// TestModule36 //
//================================================================================//
public void TestModule36()
{
MessageBox.Show("TestModule36");
}
1] When the user sees this form in the Normal Mode, they shall be unable to edit/change the data-related textboxes, comboboxes, checkboxes, radio buttons, etc. Buttons Save and Cancel will have no meaning or activation. All other buttons shall be activated.
2] When the user sees this form in the Edit Mode, they shall be able to edit/change the data-related textboxes, comboboxes, checkboxes, radio buttons, etc. Buttons Save and Cancel will be activated.
3] If users could lock a record and then move to the next record, this would increase the probability that the user would be able to move to the next record and forget to come back and finish editing the previous record. It is important to make sure that the user continues to focus on the business of editing the current record; for this reason we shall disable all of the buttons except Save and Cancel. We shall disable all comboboxes, checkboxes, and radio buttons that do not relate to the data.
4] A change in color should be
present so thatthe user can easily discern between the two states; continue
reading and this shall make more sense as you complete the steps and code below.
1] As mentioned earlier, button btnTesting was added to our form for diagnostic testing during the development cycle; it shall either be removed from or hidden on the final system. We are adding a second diagnostic button, called btnEditMode, to our Users form; it too shall either be removed from or hidden on the final system. (See Below!)

2] Add function EditMode to the Users form. We shall develop the code that goes with this shortly. (See Below!)

3] Double-Click on button btnEditMode to create the event logic which shall enable btnEditMode to execute function EditMode( ); add the function call. (See Below!)

4] We are adding a third diagnostic button, called btnNormalMode, to our Users form; it too shall either be removed from or hidden on the final system. (See Below!)

5] Add function NormalMode to the Users form. We shall develop the code that goes with this shortly. (See Below!)

3] Double-Click on button btnNormalMode to create the event logic which shall enable btnEditMode to execute function NormalMode( ); add the function call. (See Below!)

1] Add the following block of code to the EditMode function. All of the Letter buttons shall be enabled.

2] Add the following block of code to the NormalMode function. Save & Cancel menustrip buttons shall be disabled..

3] Add the following block of code to the NormalMode function. All menustrip buttons, except Save & Cancel, shall be disabled..

4] Add the following block of code to the NormalMode function. All Navigation buttons shall be enabled with their respective images.

5] Add the following block of code to the NormalMode function. All data related textboxes shall be ReadOnly so that their contents may not be altered.

6] Add the following block of code to the NormalMode function. All data related checkboxes and comboboxes shall be disabled so that their contents may not be altered.

7] Add the following block of code to the NormalMode function. All non-data related checkboxes and comboboxes shall be enabled so that their contents may be altered.

8] Execute the program. Push the Normal Mode button. Note that Save and Cancel have been disabled. Note the user can not change the Data (First, Last, Middle Initial, UserName, Password, Major, or Administrator). Note that the Navigation buttons, Letter Buttons, Order By ComboBox, and Select ComboBox are all functional. CloseWindow is the only one of the MenuStrip buttons that are functional; it works! (See Below!)

1] Add the following block of code to the EditMode function. All of the Letter buttons shall be disabled. Once the user has locked a record to edit, we do not want that user to be able to move on to another user.

2] Add the following block of code to the EditMode function. Save & Cancel menustrip buttons shall be enabled..

3] Add the following block of code to the EditMode function. All menustrip buttons, except Save & Cancel, shall be enabled..

4] Add the following block of code to the EditMode function. All Navigation buttons shall be disabled with their respective disabled images.

5] Add the following block of code to the EditMode function. All data related textboxes shall no longer be ReadOnly; the user shall be able to alter the data.

6] Add the following block of code to the EditMode function. All data related checkboxes and comboboxes shall be enabled; the user shall be able to alter the data.

7] Add the following block of code to the EditMode function. All non-data related checkboxes and comboboxes shall be enabled so that their contents may be altered.

8] Execute the program. Push the Edit Mode button. Note that Save and Cancel have been enabled. Note the user can change the Data (First, Last, Middle Initial, UserName, Password, Major, or Administrator). Note that the Navigation buttons, Letter Buttons, Order By ComboBox, and Select ComboBox are no longer functional. None of the MenuStrip items are functional. (See Below!)

1] Change the UserClassDiagnosticLevel to 25. (See Below!)

2] Copy the code from TestModule24 to TestModule25 and make the following changes.
3] Execute the Users Test Module. You should be able to see the application change modes. (See Below!)

1] Many different librarians will have the ability to scroll through our User records with the Users.cs form above. Occasionally one of the librarians will want to make some changes to a given record. It is possible that two or more concurrent users will want to make changes to the same record at the same time. If we allow one user to lock the record - make changes - and then free the record, the potential to avoid concurrent user anomaly problems is optimized.
2] Problem 1: Many databases have the ability to lock a database table, but few have the ability to lock a table record. Locking the entire database greatly limits the functionality of other users!
3] Problem 2: Even the very few databases which offer the ability to lock a record, have no mechanism to determine who has locked the record.
4] Problem 3: Few of the databases have the utilities necessary to easily do timing calculations; we generally have to write some of our own functions.
5] Problem 4: Most computers do not have their clocks synchronized; users may be in different time zones. It is critical that all timings be relative to the SQL Server, as opposed to the user's personal computer.
6] The Record Lock-Out strategy demonstrated in this tutorial
can be used in both web-oriented databases and multi-user network databases.
The logic can be applied to databases, MySQL, DBase, and other database engines.
1] Now to functionalize this. Change the UserClassDiagnosticLevel to 26. (See Below!)

2] Write function ComputeNoDays which accepts a DateTime Object and explicitly returns the number of days that have passed since Dec. 31st, 1999; thus starting our system timing with the year y2k.

3] Write function ComputeNoSeconds which accepts a DateTime Object and explicitly returns the number of seconds that have passed thus far on the last day.

4] Copy the code from TestModule25 to TestModule26 and make the following changes.

5] Execute the Users Test Module. Examine the test module carefully to make sure that you understand what it is attempting to do. Examine the output carefully to make sure that it matches that below and is consistent with the test code above.

6] The first block of code (1/1/2000) is 1 Second into Day 1 into y2k.
7] The second block of code (1/1/2001) is one minute two seconds [60+2] into is Day 367 [366+1] into y2k.
8] The third block of code (1/3/2002) is one hour [60x60 seconds] into is Day 734 [366 + 365 +3] into y2k
9] Suppose a user were to lock a record [on 1/1/2001 12:01:02] for 120 seconds. The two functions above enable us to get CurrentNoDays = 367 and CurrentNoSeconds = 62. The two functions above enable us to calculate LockExpireDay = 367 and LockExpireSecond = 182; when another user attempts to lock this same record, we shall use these functions, with the current date, to see if a record lock has expired. We shall discuss this later.
1] Now to functionalize this. Change the UserClassDiagnosticLevel to 27. (See Below!)

2] Write function GetCurrentServerDateTime which explicitly returns a C# DateTime object with the current server time.

3] Write function GetCurrentServerDateTimeStr which explicitly returns a string object with the current server time.

4] Copy the code from TestModule26 to TestModule27 and make the following changes.

5] Execute the Users Test Module. Examine the test module carefully to make sure that you understand what it is attempting to do. Examine the output carefully to make sure that it matches that below and is consistent with the test code above. There should be about a five second span between the two sets of data.

1] Now to functionalize this. Change the UserClassDiagnosticLevel to 28. (See Below!)

2] Write function GetDaySecond which converts the date object to the LockDay and LockSecond used by our Record-Lock system.

3] Write function GetDaySecondStr which converts the date, in string format, to the LockDay and LockSecond used by our Record-Lock system.
4] Copy the code from TestModule27 to TestModule28 and make the following changes.

5] Execute the Users Test Module. Examine the test module carefully to make sure that you understand what it is attempting to do. Examine the output carefully to make sure that it matches that below and is consistent with the test code above. I have pasted the equivalent output from TestModule26 for easy comparison.

1] Now to functionalize this. Change the UserClassDiagnosticLevel to 29. (See Below!)

2] Write the three overloads function GetExpiredDaySecond which uses the NoSecondsToLock to compute the LockExpireDay and LockExpireSecond used by our Record-Lock system. The three overloads provide a variety of interfaces by which we may solicit the LockExpireDay and LockExpireSecond.

3] Copy the code from TestModule28 to TestModule29 and make the following changes.

4] Execute the Users Test Module. Examine the test module carefully to make sure that you understand what it is attempting to do. Examine the output carefully to make sure that it matches that below and is consistent with the test code above. The three function overloads provide multiple ways in which to calculate the the LockExpireDay and LockExpireSecond used by our Record-Lock system.
1] Now to functionalize this. Change the UserClassDiagnosticLevel to 29. (See Below!)

2] Write the three overloads function GetExpiredDaySecond which uses the NoSecondsToLock to compute the LockExpireDay and LockExpireSecond used by our Record-Lock system. The three overloads provide a variety of interfaces by which we may solicit the LockExpireDay and LockExpireSecond.

3] Copy the code from TestModule28 to TestModule29 and make the following changes.

4] Execute the Users Test Module. Examine the test module carefully to make sure that you understand what it is attempting to do. Examine the output carefully to make sure that it matches that below and is consistent with the test code above. The three function overloads provide multiple ways in which to calculate the the LockExpireDay and LockExpireSecond used by our Record-Lock system.
1] Several of our generic functions were written with thoughts of one database connection and one datatable for that database. Since our LibraryApp is going to use at least two databases (LibraryApp and RecordLock), we shall modify/update some of our generic functions to make them even more generic.
1] In an attempt to create functions that are generic, we are going to define the DatabaseName and TableName at the top of each and every form that shall use these utilities.
2] Change the declarations, at the top of Users.cs, to be as follows: (include the comments!)
c
3] Execute LibraryApp; it should continue to bring up the
Users.cs form.
1] As is often the case in computer science, we have to modify functions as an application increases in functionality. Since we are going to use two databases (LibraryApp and RecordLock) we are going to need two connection objects; both Conn and RL_Conn are defined above.
2] To better distinguish between the two connection objects, replace all occurrences of Conn with LA_Conn (LibraryApp Connection); do a global replacement as illustrated below. (See Below!)

3] Our first SQLAuthenticationConnection function is written to connect only to LA_Conn. We could write a similar function to connect to RL_Conn, but this would be really redundant. In reality, our next application might need to connect to three or four database.
4] A better solution is to make function SQLAuthenticationConnection more flexible by passing the Connection object. Change your code to that below!
5] Of course when we make this change, none of our previous calls to this
function will compile. Change each and every occurrence of SQLAuthenticationConnection(

to
SQLAuthenticationConnection("Net1\\SQLExpress", "LibraryApp", "student", "student", ref LA_Conn);
6] Execute LibraryApp; it should continue to bring up the Users.cs form; test all controls to assure that they still work.
7] Adjust the commenting on any of the documentation boxes in Users.cs.
8] Execute LibraryApp; it should continue to bring up the
Users.cs form; test all controls to assure that they still work.
9] Function SQLAuthenticationConnection has been made more flexible and
more generic by passing the Connection object. It can be used with both of the
databases in our project.
1] Since we are going to use two databases (LibraryApp and RecordLock) we are going to need two datatable objects to perform our queries; both dt and RL_dt are defined above.
2] To better distinguish between the two connection objects, replace all occurrences of dt with LA_dt (LibraryApp DataTable); do a global replacement as illustrated below. (See Below!)

3] Our first FillDataTable function is written to connect only to LA_dt. We could write a similar function to connect to RL_dt, but this would be really redundant. In reality, our next application might need to connect to three or four database.
4] A better solution is to make function FillDataTable more flexible by passing the Connection object. Change your code to that below!
5] Of course when we make this change, none of our previous calls to this
function will compile. Below you will see just one of the many calls to function
FillDataTable. FillDataTable(Query, 
You will have to add LA_Conn to each as illustrated below.
FillDataTable(Query, ref dt, LA_Conn);
6] Execute LibraryApp; it should continue to bring up the Users.cs form; test all controls to assure that they still work.
7] Adjust the commenting on any of the documentation boxes in Users.cs.
8] Execute LibraryApp; it should continue to bring up the
Users.cs form; test all controls to assure that they still work.
9] Function FillDataTable has been made more flexible and more generic by
passing the Connection object. It can be used with both of the databases in our
project.
1] We shall need a connection to database RecordLock. Change function Users_Load to be as follows:

2] Execute LibraryApp; it should continue to bring up the
Users.cs form; test all controls to assure that they still work.
1] In Part 3 of this tutorial set, we created a database called RecordLock.
2] The structure may be seen below:

1] Now to functionalize this. Change the UserClassDiagnosticLevel to 28. (See Below!)

2] Write GetDaySecond which converts a Date object into our LockDay and LockSecond format that will be used in our Record Lock System.

3] Write GetDaySecondStr which converts a String formatted date into our LockDay and LockSecond format that will be used in our Record Lock System.

4] Copy the code from TestModule27 to TestModule28 and make the following changes.

5] Examine the test module carefully to make sure that you understand what it is attempting to do. Examine the output carefully to make sure that it matches that below and is consistent with the test code above.

1] Now to functionalize this. Change the UserClassDiagnosticLevel to 29. (See Below!)

2] Write function GetExpireDaySecond which converts the passed date and NoSecondsToLock into the LockDay and LockSecond format that will be used in our Record Lock System. Note that there are a number of different function overloads to make the using this utility more convenient to utilize.

3] Copy the code from TestModule28 to TestModule29 and make the following changes.

5] Examine the test module carefully to make sure that you understand what it is attempting to do. Examine the output carefully to make sure that it matches that below and is consistent with the test code above.



1] Now to functionalize this. Change the UserClassDiagnosticLevel to 30. (See Below!)

2] Write function LockExpired which explicitly returns true if the lock time has expired; otherwise sets NoSecondsTillLockExpires and returns false.

3] Copy the code from TestModule29 to TestModule30 and make the following changes.

4] Execute the Users Test Module. Examine the test module carefully to make sure that you understand what it is attempting to do. Examine the output carefully to make sure that it matches that below and is consistent with the test code above.

1] There are a number of basic support functions to add and delete record lock requests included in this section.
2] The real value of most of the functions in this section will become much more obvious in Part 5 of this tutorial.
1] Now to functionalize this. Change the UserClassDiagnosticLevel to 31. (See Below!)

2] Write function InsertRecordLockRequest1 which (1) Delete any previous lock requests for this table of this database by this user, (2) Delete Any Expired Lock Requests For This Record, (3) Insert this Record-Lock Request into the Locks Queue.


3] Before you execute the test module, load Microsoft SQL Server Management Studio Express. Delete any and all of the records from table Locks. Minimize Microsoft SQL Server Management Studio Express.

4] Copy the code from TestModule30 to TestModule31 and make the following changes.

5] The first block of code should create eight records. Our Date and Expire times will be different. There are four request LibraryApp->Users->1 and four request for LibraryApp->Books->2. There will then be a 25 second delay in which you can refresh the contents of the Locks database [with SQL Management Studio Express].. (See Below)

6] The next call to InserRecordLockRequest1 should
InsertRecordLockRequest1(
"LibraryApp", "Users", 1, "5", 30);(1) delete all previous lock requests for any record in LibraryApp->Users by User #5 [there are none]
(2) delete all expired lock requests for LibraryApp->Users->1 [records 1 & 2 are expired]
(3) insert a 30 second lock record LibraryApp->Users->1 by User #5 [record 9] (See Below)

7] The next call to InserRecordLockRequest1 should
InsertRecordLockRequest1(
"LibraryApp", "Users", 13, "4", 30);(1) delete all previous lock requests for any record in LibraryApp->Users by User #4 [record 4]
(2) delete all expired lock requests for LibraryApp->Users->1 [there are none]
(3) insert a 30 second lock record LibraryApp->Users->13 by User #4 [record 9] (See Below)

6] Although the program output is hardly profound, it does
help to confirm the LockStartDate, LockEpireDay, and LockExpireSecond for
TestModule31.


1] Now to functionalize this. Change the UserClassDiagnosticLevel to 32. (See Below!)

2] Write function DeleteUserRecordLockRequests which deletes all of the record lock request by this user for this table of this database.

3] Write function DeleteAllRecordLockRequests which deletes all of the record lock requests

4] Before you execute the test module, load Microsoft SQL Server Management Studio Express. If you have just completed the TestModule31, your database should have seven records similar to those below.

5] Copy the code from TestModule31 to TestModule32 and make the following changes.

6] The next call to DeleteUserRecordLockRequests should
DeleteUserRecordLockRequests()
(1) delete all previous lock requests for the default user (UserID=4) on the default table (Users) of the default database (LibraryApp) [Delete record 10] (See Below)

7] The next call to DeleteUserRecordLockRequests should
DeleteUserRecordLockRequests(
"LibraryApp", "Books", "2")(1) delete all previous lock requests for UserID=2 on table (Books) of database (LibraryApp) [Delete record 6] (See Below)

8] The next call to InserRecordLockRequest1 should

9] The program output is hardly profound.


1] Now to functionalize this. Change the UserClassDiagnosticLevel to 33. (See Below!)

2] Write function DeleteExpiredRecordLockRequestsForRecord which deletes all expired record lock requests for this record in this table of this database.

3] Write function DeleteTableLockRequests which deletes all of the record lock requests for this table of this database.

4] Before you execute the test module, load Microsoft SQL Server Management Studio Express. If you have just completed the TestModule32, your database should have no records.

5] Copy the code from TestModule32 to TestModule33 and make the following changes.


6] The code above, should add the the following 11 records and then delay for 425 seconds. Bring up Microsoft SQL Management Studio Express and check out the eleven records. Once again the Date, LockExpireDay, and LockExpireSecond will vary. (See Below)

7] The next call to DeleteExpiredLockRequestsForRecord should
DeleteExpiredLockRequestsForRecord (
"LibraryApp", "Users",1)(1) delete all expired lock requests for LibraryApp->Users->1 [records 11 & 12]

8] The next call to DeleteExpiredLockRequestsForRecord should
DeleteExpiredLockRequestsForRecord(3)
(1) delete all expired lock requests for LibraryApp->Users->3 [records 15 & 16]

9] The next call to DeleteExpiredLockRequestsForRecord should
DeleteTableLockRequests(
"LibraryApp", "Books")(1) delete all lock requests for LibraryApp->Books [records 18, 19, 20, & 21]

10] The program output is hardly profound.


1] Now to functionalize this. Change the UserClassDiagnosticLevel to 34. (See Below!)

2] Make a copy of funciton InsertRecordLockRequest1; call it function InsertRecordLockRequest. By using some of our other functions, we can create a function InsertRecordLockRequest that is shorter and simpler than InsertRecordLockRequest1.

3] The call to DeleteAllLockRequests, at the top of the test module, will leave table Locks with no records.

4] Copy the code from TestModule32 to TestModule33 and make the following changes.

5] The code above, should add the the following 11 records. Once again the Date, LockExpireDay, and LockExpireSecond will vary. (See Below)

10] The program output is hardly profound.


1] Download and add the following three files to folder Library-Net-Multi-User-4
2] You can see the files in the project folder below.

3] Time to add the form to our project. Right mouse click on LibraryApp ==> Select Add ==> Select Existing Item... (See Below!)

4] Select the three CustomMessageBox files. Using the mouse, select/push the Add button. (See Below!)

5] Execute the LibraryApp to make sure that all compiles well.
1] Now to functionalize this. Change the UserClassDiagnosticLevel to 35. (See Below!)

2] Write FillRecordLockDataSet which fills datatable RL_dt with a queue of all of the lock request for the RecordID.

3] Write DisplayRecordLockDataSet which uses the CustomMessageBox to display datatable RL_dt (which represents the queue of all of the lock request for the RecordID).

4] Copy the code from TestModule27 to TestModule28 and make the following changes.

5] Examine the test module carefully to make sure that you understand what it is attempting to do. Examine the output carefully to make sure that it matches that below and is consistent with the test code above.

6] Note the consistency with the five database records from the actual database below.

1] Now to functionalize this. Change the UserClassDiagnosticLevel to 36. (See Below!)

2] Write function CompleteRecordLock which will set the record lock request to True.

3] Copy the code from TestModule35 to TestModule36 and make the following changes.

4] Execute the Users Test Module. The first block of code, in the test module, will delete all of the records in the record locks queue. It will then create 11 records requests. Although our times and record ID's will vary, the order of our record request should be logically the same. A snap-shot of my database can be seen below. We are going to attempt to lock the first record request for RecordID = 1 (that would be ID = 47).

5] The queue of record lock requests reveals that we have five request for RecordID = 1. You can see that ID = 47 is the first request in our queue. (See Below!)

6] CompleteRecordLockRequest is then executed for record ID = 47. This should update the times and set Locked to 'T'. (See Below!)

7] TAnother look at the queue of record lock requests reveals that we have still have five request for RecordID = 1. You can see that the Locked flag for ID = 47 has been set to 'T'. (See Below!)

8] The program output is hardly profound.


8] This tutorial contains a number of generic database functions that shall be used in other tables and other applications. We shall make extensive use of these utilities in Part 5 of this Tutorial.
1] There is not much opportunity for you to put really see the value of these utilities until we complete Part 5 of this tutorial.
1] Gradually phase in the following items from Users.cs to Books.cs. I included my test code (and made modifications to make it work), but this is optional. Make sure that the Books.cs includes the following:
1] In an attempt to create functions that are generic, we are going to define the DatabaseName and TableName at the top of each and every form that shall use these utilities.
2] Change the declarations, at the top of Books.cs, to be as follows: (include the comments!)

1] Do a global replacement as illustrated below. (See Below!)

2] A better solution is to make function SQLAuthenticationConnection more flexible by passing the Connection object. Change your code to that below. You are certainly welcome to copy-paste the function from Users.cs.

3] You will need to change all previous calls to this
function will compile. Change each and every occurrence of
SQLAuthenticationConnection
to
SQLAuthenticationConnection
("Net1\\SQLExpress", "LibraryApp", "student", "student", ref LA_Conn);
1] Do a
global replacement as illustrated below. (See Below!)

2] A better solution is to make function FillDataTable more flexible by passing the Connection object. Change your code to that below! You are certainly welcome to copy-paste the function from Users.cs.
3] You will need to change all previous calls to this
function will compile. Change each and every occurrence of
FillDataTable

to
FillDataTable (Query, ref dt,
LA_Conn);
4] Execute LibraryApp; it should continue to bring up the
Users.cs form; test all controls to assure that they still work.
1] Change the code for Books_Load to be as follows: . (See Below!)

1] Add btnEditMode and btnNormalMode to Books.cs. You are welcome to copy-paste them from Users.cs. (See Below!)

2] Copy functions EditMode to Books.cs and adjust the data fields accordingly. The results should be as follows:

3] Copy functions NormalMode to Books.cs and adjust the data fields accordingly. The results should be as follows:

1] Gradually phase in the following items from Users.cs to Books.cs. I included my test code (and made modifications to make it work), but this is optional. Make sure that the Books.cs includes the following:
//================================================================================//
// ComputeNoDays //
//================================================================================//
//================================================================================//
// ComputeNoSeconds //
//================================================================================//
//================================================================================//
// GetCurrentServerDaySecondStr //
//================================================================================//
//================================================================================//
// GetCurrentServerDateTime //
//================================================================================//
//================================================================================//
// GetDaySecond //
//================================================================================//
//================================================================================//
// GetDaySecondStr //
//================================================================================//
//================================================================================//
// GetExpireDaySecond //
//================================================================================//
//================================================================================//
// LockExpired //
//================================================================================//
//================================================================================//
// DeleteUserRecordLockRequests //
//================================================================================//
//================================================================================//
// DeleteAllLockRequests //
//================================================================================//
//================================================================================//
// DeleteExpiredLockRequestsForRecord //
//================================================================================//
//================================================================================//
// DeleteTableLockRequests //
//================================================================================//
//================================================================================//
// InsertRecordLockRequest //
//================================================================================//
//================================================================================//
// FillRecordLockDataSet //
//================================================================================//
//================================================================================//
// DisplayRecordLockDataSet //
//================================================================================//
//================================================================================//
// CompleteRecordLock //
//================================================================================//
2]
Compile your program regularly as you add the functions above.3] If you copied the test functions and tested your functions individually, you will have some degree of confidence in your code; you will have make modifications to get the code to compile.