2. Building Database Applications
In this chapter, we shall devote
our time and energy writing applications that interact with databases. We would
commence by creating a new project. Though we had assured you that we would
demonstrate all the steps needed to build an application, by displaying the
corresponding screens, we shall avoid displaying the screens that have already
been shown in the earlier chapter. This will indeed save a lot of book space.
However, we shall explain all the steps, but with only the fresh screens, that
have not been shown earlier. If the screen that you obtain does not match with
our explanation, the possibilities could be that either you or we have gone
astray somewhere. At moments like these, we would request you to immediately
dispatch an email to us, describing the problem.
As was learnt in the previous
chapter, in order to create a new project, we need to click on the File menu
option and select New Project. At the New Project dialog box, we have to select
the following:
• Visual C# Projects in the Project Type pane.
• Windows Application in the Templates pane.
Believe it or not, we have
assigned the name z1 to the project after consulting an astrologer. Further,
because we want all our projects to be saved in a single subdirectory, we have
chosen the sub-directory 'vijay'. Hence, the location becomes c:\vijay. If a
project has not been closed gracefully while exiting from this product, on
restarting Visual Studio.Net, an extra line gets displayed, containing two
radio buttons relating to Solution. For the moment, we shall take it for
granted that a Solution is the same as a Project. The radio button for Close
Solution is selected. Had we closed the project while exiting, this line would
not have appeared, as was observed in the first example. Screen 2.1 displays
the options very visibly.
|
Screen 2.1 |
Click on the OK button to close
the dialog box. Also, make sure that the Properties window is visible on the
left, while the Toolbox window is visible on the right. Both these options are placed
under the View menu option, lest you forget.
The Toolbox has a tab called
Data. If we click on this tab, we will see a large number of controls that may
be employed to work with a large number of data sources or databases, as is
seen in screen 2.2.
|
Screen 2.2 |
Thus, the toolbox segregates the
controls, depending upon where they are going to be used and their functionality.
Earlier on, we had worked on the Windows Forms controls, where we had used
controls such as the button, the progress bar, the track bar, etc. In this
chapter, we shall focus on the Database category, where we would concentrate on
the controls pertinent to Databases only.
At the outset, we commence with
an object that understands communication and interaction with databases. In the
Visual Studio.Net world, this object is termed as a Data Adapter. There are two
Data Adapters displayed in the above list, i.e. an OleDbDataAdapter and an
SqlDataAdpater. They are similar in functionality, barring a minor difference.
The OleDbDataAdapter is generic in nature, which explains why it can speak to
any database or any data source. On the other hand, an SqlDataAdapter can
communicate only with SQL Server, which is a database server from Microsoft. As
a result, the SqlDataAdapter can retrieve data relatively faster than
OleDbDataAdapter.
We select the OleDbDataAdapter
in the toolbox and then drag and drop it onto the form. Surprisingly, it places
itself at the bottom of the form, and then, a screen from the Data Adapter
Configuration wizard, appears with some text contained in it. This is shown in
screen 2.3.
|
Screen 2.3 |
To create an Adapter, a large
number of steps need to be executed. As these steps are required to be performed
in a specific sequence, the wizard lends a helping hand in guiding us through
this process. It also provides a GUI to accept the relevant values from us.
The Adapter basically requires
two types of information:
• The first one refers to the database where the data is stored.
• The second one refers to the data from this database.
After reading the opening titles
in the first screen of the Wizard, we move on to the next screen by clicking on
the Next button. Our screen should bear resemblance to screen 2.4.
|
Screen 2.4 |
Here, we are required to provide
information about the connection that we desire to use, in order to connect to
the database. The wizard specifies a Server Explorer, which we shall delve upon
shortly. Since there are no connections at present, we create a new connection.
To do so, we need to click on the New Connection button, to obtain a screen
similar to screen 2.5.
|
|
Screen 2.5 |
Screen 2.6 |
It is within this dialog box,
that all the connection details are required to be keyed-in. We are not
required to specify all of them. The name of the server can be left blank,
since the Connection has the ability to internally figure out the server that
it has to talk to. The next textbox requires the 'username' and 'password'.
This has been furnished for security reasons, as the access to the database
should be restricted only to a chosen few. The most important user that is
created while installing the SQL Server database is granted an id of 'sa'. The
password assigned to this user has been left blank at the time of installation.
Thus, we enter 'sa' as the username and click on the check box that says 'Blank
Password'. On doing so, the password textbox gets disabled.
A database can contain numerous
tables, since data is normally stored in tables. Therefore, logical
organization of the tables into databases, becomes the inescapable necessity of
the hour. For example, tables pertaining to a financial application could be
stored in a database named 'finance', while those pertaining to production
could be stored in another database named 'production'.
While installing Visual
Studio.Net, Microsoft creates a large number of these databases. One such
database is called NorthWind. Clicking on the down arrow for the database,
displays a list of databases as shown in screen 2.6. Remember, we have not
created any of these databases. They have been provided as an accessory, along
with the product.
The dialog box provides a button
labeled 'Test Connection', to facilitate testing of the connection to the
database. During testing, various facets are authenticated, such as:
· Availability of the database.
· Validation of the username.
· Validation of the password.
Thus, clicking on the button
results in verification as to whether the user can connect to the database
server or not. Here, we get a message box indicating success, as shown in
screen 2.7.
|
Screen 2.7 |
If you change the name of the user
to a non-existent one, such as sa100 and then click on the Test Connection
button, an error will be generated. Working with wizards can indeed prove to be
very beneficial, since it is possible for us to verify whether we are on the
right track or not, at every stage.
Click on the OK button of the
Message Box followed by the OK button of the Dialog Box. The screen 2.8 is the
same as screen 2.4, with which the wizard started, except for the fact that
VMUKHI.Northwind.dbo is specified as the active data connection in the listbox.
NorthWind is the name of the database with which we want to establish a
connection.
|
|
Screen 2.8 |
Screen 2.9 |
Select the Next button and the
screen changes to the one displayed in screen 2.9.This screen requires us to select
the method to be used, to fetch data from a database. There is an international
standard called SQL or Structured Query Language. All databases in the world
use and understand SQL. This standard or language has a list of words that
fetch data from databases.
Another way of retrieving data
is, by using Stored Procedures. It refers to executing program code residing on
the server. Here, we select the SQL option, which is the default selection, and
then, click on Next button to arrive at screen 2.10.
|
|
Screen 2.10 |
Screen 2.11 |
In this screen, the blinking
cursor in the big textbox indicates, that we are expected to write an SQL
statement to retrieve data from the database. Most of us are obviously not
conversant with SQL at this point in time, nor are we aware of the tables and
fields present in the NorthWind database. Don't feel vexed, as the Query
Builder comes to the rescue. Click on the Query Builder button and the display
changes to screen 2.11.
A dialog box pops up, exhibiting
a list of tables, from where the data can be fetched. Thus, there is no need to
examine the tables that are present in the database. We are interested in the
'Customers' table. So, we select it and then click on the Add button. Clicking
on the Add button results in the display of a graphical block in the
background, as shown in screen 2.12. As we are not interested in any more
tables from the database, we select the Close button.
|
|
Screen 2.12 |
Screen 2.13 |
The screen now gives a clear
indication that the graphical block refers to the columns in the table. We
chose the first three fields, i.e. CustomerID, CompanyName and ContactNameby by
selecting the relevant check boxes. Once this is done, notice the changes in
the screen. We have depicted this in screen 2.13. This screen displays 4 panes.
The first pane contains the tables with their fields. Presently, we are
interested in working with a single table. The second pane explicitly displays
the selected fields; while the third pane is an SQL Select statement that is
generated, based on the fields selected. This statement is given below:
SELECT CustomerID, CompanyName, ContactName
FROM Customers
The fourth pane is blank. We
will discuss it at an appropriate time. Click on the OK button and you will
come to screen 2.14, where the SQL statement is clearly visible.
|
Screen 2.14 |
The power of SQL comes to the
fore, when there are multiple tables.
The word SELECT in SQL, is the only command that can be used to retrieve
data from a database. This command is then provided with the fields from the
table. The word FROM is a reserved word, and is always followed by the name of
the table. In our case, it is Customers. We leave it upto you to decide whether
you would like to write the above SQL statement yourself, or you wish to hire
the services of the Query Builder, to do it for you. Click on the Next button
to proceed to the next screen.
|
Screen 2.15 |
A click on Next button brings up
screen 2.15, which distinctly indicates to us that everything went off well.
The Adapter has created the following:
• The Select statement, to extract data from the database.
• The Update statement, to make changes in the data of the database.
• The Insert statement, to insert data into the database.
• The Delete statement, to delete data from the database.
|
Screen 2.16 |
Clicking on the Finish button,
brings us to screen 2.16. The wizard not only creates an Adapter object, but also
a Connection object. Visual Studio.Net names the Adapter object as
oleDbDataAdapter1, and the Connection object as oleDbConnection1. All is not
over yet, as we have still to create another object that will store all the
data that is to be retrieved. This new object or container is called a DataSet.
To create a DataSet, we simply
click on the Data menu and select the Generate Dataset menu option, as is seen
in screen 2.17.
|
Screen 2.17 |
This is one menu option that
does not hold too many options. After we click on this first option, a dialog
box gets displayed on the screen, as we see in screen 2.18.
|
Screen 2.18 |
The first question that is
always tossed at us is, whether we want to reuse an earlier created data set, or
we wish to create a new one.
Bear in mind that Visual
Studio.Net would like you to reuse things, so that you do not commit mistakes,
while re-creating them over and over again.
The dialog box asks us to enter
the name of the DataSet. DataSet1, which is the name that is specified here,
has to be provided at the time of referring to the DataSet. So, you need to
commit it to your memory. Further, a list of tables that this data set would
comprise of, is provided in the next block.
Presently, only the Customers
table is available. So, DataSet1 refers to this table. However, a DataSet
object can represent a large number of tables.
A list of tables associated with
the Adapter, is displayed in this block. The checkbox labeled as 'Add to
Designer' is left checked, and then we click on the OK button. This transports
us to screen 2.19.
|
Screen 2.19 |
Upto this point, the DataAdapter
control has created the Connection control, and the DataSet control has used a
table from the DataAdapter.
As the next step, we select
Windows Forms in the toolbox window, in order to see the Forms based control.
Then, we choose a data grid object and drag and drop it onto the form. This is
clearly seen in screen 2.20.
|
Screen 2.20 |
The screen displayed, has the control
enlarged vertically and horizontally, in order to facilitate the display of
more data in the data grid control. Press F5 to run the program.
|
Screen 2.21 |
At this stage, screen 2.21 shows
up, which is an empty data grid, with neither any column names nor any data.
This is a consequence of our not having specified the source from where the data
is to be retrieved, in the data grid control.
Close the window and select the
data grid in the Design Form. Thereafter opt for the Data Source property.
When you click on the down
arrow, a list of DataSets is displayed. Presently, our screen 2.22 depicts only
one data set, which is the one that we had created most recently.
|
Screen 2.22 |
Thus, we have clearly specified
that the data grid control will use the data, which is stored in the dataset
named dataSet11. Also, note that the form design changes, to display a + sign
within it, screen 2.23. Let us now run the program to see what transpires.
|
|
Screen 2.23 |
Screen 2.24 |
The screen 2.24 displays a plus
sign, which is similar to what was seen in the Form Design. Once we click on
the + sign in this screen, the + sign becomes a - sign and the label of
Customers is displayed, as shown in screen 2.25.
|
|
Screen 2.25 |
Screen 2.26 |
The label Customers is displayed
as an aftermath of the fact that, we have only one table called Customers in
the DataSet. In the situation where we would have had more tables, all of them
would have been displayed, one below the other.
Selecting or clicking on the
hyperlink of Customer will lead us to screen 2.26, where three fields are
displayed, with each of them having a value of Null. The three fields are the
three column names that we had chosen while generating the SELECT statement.
Also, the title is shown as DataSet1, and not as dataSet11.
To display the data in the
datagrid control, first close the running application, and then, make sure that
the Data grid is selected.
If you select the property of
DataMember and click on the down arrow, you will see screen 2.27.
|
Screen 2.27 |
The Data Member displays a list of
tables. We can see only one table, since only a single table named 'Customers'
is present in our dataset. This Data Member property can be selected, only
after we have specified a data set as a Data Source.
Select the Customers table and
notice the change in the Data Grid on the form, this is shown in screen 2.28.
|
Screen 2.28 |
Thereafter, run the application and
you will come across screen 2.29.
|
Screen 2.29 |
The + sign is no longer visible,
and the list of tables has also vanished. All this can get pretty confusing for
a user. The data grid directly displays the columns from the Customer table.
This is because, the Data Grid control uses the DataSource member to find the
tables contained in it, whereas the DataMember property, when initialized,
refers to a specific table in the dataset.
Leaving the DataMember property
blank, will again result in a display of a list of available tables to select
from.
The columns in the field still
depict null values. In order to display data, we first take a button from the
toolbox and then drag and drop it onto the form.
|
Screen 1.30 |
Then, we double-click on the
button to reach the Code Painter, where we write the following line of code:
oleDbDataAdapter1.Fill(dataSet11);
The Adapter control has a
function called Fill, which accepts a data set as a parameter. It then associates
data with this data set. As of now, the data set control does not display any
data. This is because the data set control is not aware of the data that it
would receive.
|
Screen 1.31 |
It is the Fill function from the
Adapter class, which connects to the database using the connection control. It
then retrieves data from the database and passes it to the data set. Once the data
set obtains the data, it can then pass it on to the Data Grid for display.
Pressing F5 would initially
display the columns without any data. When we click on the button with the
default label button1, the Fill function does its job and populates the data
grid. Therefore, the screen displays the data grid with values obtained from
the customer database, as seen in screen 2.32.
|
Screen 2.32 |
One simple click executes the
task of loading the data grid. It cannot get any easier!
The next task on hand is to
allow the user to change the data. Obviously, any such change should be
reflected in the database too. To achieve this goal, we once again introduce a
button from the toolbox, and add the following line of code to it:
oleDbDataAdapter1.Update(dataSet11);
In the Adapter control, there is
another function called Update, which is similar to the Fill function. This
function takes the data associated with a data set and changes it permanently
in the database, thereby, updating the database.
On running the application, we
see two buttons and the datagrid control. The first button is used to display
the data from the database, while the second one is used to update the
database. Select the second record and change the name of the company to 'Vijay
Mukhi'. Thereafter, click on the button labeled button2. This is shown in
screen 2.33.
|
Screen 2.33 |
Clicking on button2 does not
achieve anything stupendous. So, how in the world are we to confirm that the
database has been updated? To do so, close the running application and re-run
it. On doing so, the second record will display the freshly modified value as
'Vijay Mukhi'.
There are a myriad other things
that we can do with a data grid. For
example, sorting a column, reversing the order of the sort, etc. Everything
regarding a data grid can be subjected to change. You can take our word for it!
Let us now proceed to another
data handling example, which demonstrates the Master-Detail Relationships.
We create a new project by
selecting File - New - Project. In the New Project Dialog Box, as usual, we
choose Visual C# projects in the Project Types pane, and Windows Application in
the Templates pane. Then, we name the application as z2 and maintain the location
as C:\vijay. Click on the OK button to arrive at the Screen Painter. You should
ensure that the ToolBox window is activated and the Properties window is
visible.
In ToolBox window, click on the
Data tab to select the OleDbDataAdapter control. As we had learnt a little
while ago, the Adapter Wizard gets activated when the Adapter control is
dragged onto the Form.
The first screen may be ignored.
So, you can simply click on the Next button. Unlike the earlier case, where a
connection was established to the NorthWind database, we now choose a different
database named the Pubs database. Therefore, click on New Connection. The user
name will be 'sa' with a blank password and Pubs will be the database for the
server. Finally, to ensure that the right settings have been selected, click on
Test Connection. Once the connection test proves successful, click on OK. We
would request you to note down the name assigned to the connection, since it
will be used from time to time in the application.
Clicking on the Next button will
take us to the Query Type dialog box. Just as before, we choose the default SQL
statements and then click on Next. In
the text area, we need an SQL statement, which you can either write yourself or
employ the services of the Query Builder for it. Either way, the final SQL
statement should be as follows:
SELECT pub_id, pub_name FROM publishers
The above SQL retrieves the id
and the name of the publisher from the table called 'publishers'. Finally,
clicking on Finish will create both, an Adapter and a Connection object. We
however do not stop at this. Click on the OleDbDataAdapter control again and
drag and drop it into the screen painter. This will create one more Data
Adapter object called oleDbDataAdapter2, and it will again display the wizard.
The first opening screen can be
skipped without any harm done. So, click on the Next button. The connection
that has been established to the pubs database, i.e. 'VMUKHI.pubs.dbo' in our
case, is displayed in the listbox. In case it is not displayed as selected,
click on the down arrow of the listbox and then select it. We would be
employing the same connection. Therefore, there is no point in recreating
another connection object.
Click on the Next button, since
we would like to use SQL statements, the default option, while accessing the
database. Clicking on the Next button brings us to a screen where we have to
specify the SQL Select statement. Enter the statement as follows:
SELECT title_id, title, pub_id, price FROM titles
The pubs database has a table
called 'titles', from where the title id of the book, its name, the publisher
id and the price are to be retrieved. Like before, we can either enter the
query ourselves or use the Query Builder.
Finally, click on the Finish
button. On doing so, you will witness something strange i.e. the adapter has
not created any new connection. This is because, the same connection is being
reused.
|
Screen 2.34 |
The next step in sequence is, to
create a Data Set Object or a Container for these two tables. So, we click on
the Data Menu and choose Generate Dataset.
|
Screen 2.35 |
In the dialog box, we select the
New radio button, and make sure that both the tables are selected, as shown in
screen 2.35. The name of the adapter changes, depending upon the table that is
involved. The checkbox next to the 'Add this database to designer' option, is
checked by default, and we do not intend to change it.
Visual Studio.Net maintains a
list of all the files generated for the current project. This is visible in a
window called the Solution Explorer. Click on the View menu and then Solution
Explorer in order to display this window, as is evident in the screen 2.36.
|
Screen 2.36 |
The Solution Explorer lists out the
files contained in the project or solution.
Solution z2 contains the
following files:
• A form named Form1.cs.
• A C# program named AssemblyInfo.cs.
• The References, which talks about all the things that the project refers to.
• An xsd, having the same name as the DataSet.
While generating a DataSet, an
xsd file is created, which stores information about the tables obtained from
the respective adapters. Thus, the file DataSet1.xsd is created. Double
clicking on the DataSet1.xsd in the Solution Explorer, results in the display
of screen 2.37.
|
Screen 2.37 |
The above screen shows fields of
the two tables belonging to the DataSet. The 'key' icon indicates the primary
keys. A primary key in a table is a field that contains unique values, thus
facilitating identification of different records. In the publishers table, each
publisher is given a unique id named pub_id, while in the title table, the
unique id is the title_id of every book.
The 'titles' table contains
records for every book, or the title that the book publishing company
publishes. Different books are bound to have different publishers. Hence, it
makes no sense to insert all the details about each publisher with each book.
At times, multiple books are published by the same publisher. As a result, we
would be storing the same data pertaining to a publisher over and over again,
leading to redundancy.
While storing the details of the
publishers in the publishers table, a unique publisher id is provided to every
publisher. In the 'titles' database, only the publisher id is provided with
each book, along with other title details.
This approach is called a
Master-Detail relationship or a Parent-Child Relationship, where 'publishers'
is the parent and the 'titles' is the child. It is also commonly referred to as
a 'One to Many Relationship', reiterating the fact that one publisher may
publish many books.
If you are observant enough, you
would have noticed that the ToolBox now carries a new tab called XML Schema.
Select the Relation object and drag-and-drop it onto the window. By merely
dropping it onto the window, an error will be generated in a message box,
conveying that the object cannot be placed at that position.
A Relation object has to be
dragged-and-dropped over the child table, which in this case, is the 'titles'
table. On doing so, you will arrive at the screen shown in screen 2.38.
|
Screen 2.38 |
The name assigned to the
relation is publisherstitles. You may change it if you so desire, but you must
commit it to memory, since it will be needed while referring to this relation
object.
Let us take a re-look at what we
have accomplished so far. The parent table is 'publishers' and the child table
is 'titles'. The field pub_id is a primary field in the 'pubs' table, whereas,
the field title_id is the primary field in the 'titles' table. The pub_id field
links both the tables. In the titles table, the pub_id field is termed as a
foreign key.
|
Screen 2.39 |
After clicking on OK, a new
screen that gets displayed is shown in the screen 2.39, where we see a relation
object joining them. You may observe that the parent table named 'publishers',
contains a circle; while the 'titles' table has three arrows pointing towards
it. This symbolizes a 'one to many relationship'.
In the Solution Explorer window,
double click on the file Form1.cs, in order to revert back to the Screen
Painter. In the ToolBox, there is a listbox in the category of Windows Forms.
Select the listbox and drop it onto the form painter. The screen 2.40 displays
the form.
|
Screen 2.40 |
The properties window of the
listbox has a property called Data Source. Clicking on the down arrow, displays
a list of data sources, as is apparent in screen 2.41. We choose the data set
named dataSet1.
|
Screen 2.41 |
A data set comprises of multiple
sets of data. To clearly select a specific data table, we must initialize the
Data Member appropriately. The listbox displays two tables, i.e. publishers and
titles. As we want a list of publishers to be displayed in the listbox, we
expand the publishers node by clicking on the + sign. Screen 2.42 and screen
2.43 depict the steps that are involved.
|
|
Screen 2.42 |
Screen 2.43 |
As it is the name of the
publisher, and not the publisher id, that we want to see displayed in the list box,
we select the pub_name field. The property Display Member now shows
publishers.pub_name (i.e. tablename dot fieldname). The screen 2.44
demonstrates this syntax.
|
Screen 2.44 |
Now, make sure that the form is
selected and then double click on it. Double clicking on any object transports
us to the Code Painter. Insert the following lines of code at the cursor
position:
oleDbDataAdapter1.Fill(dataSet11);
oleDbDataAdapter2.Fill(dataSet11);
This code gets executed whenever
the form loads on. The Fill statement, as we had learnt earlier, fills up the
Dataset with data. A point of significance here is that, each adapter fills up
one table in the data set.
When we run the program by
pressing the F5 key, we see a listbox that contains the names of publishers
from the 'publishers' table. This is shown in screen 2.45.
|
Screen 2.45 |
Close the running application,
in order to arrive at the Screen Painter.
Select a data grid from the Windows Form category in the toolbox, and thereafter,
drag-and-drop it onto the Screen Painter. You should size the data grid and
listbox as shown in screen 2.46.
|
Screen 2.46 |
Then, select the Data Source
property and choose the data set dataSet11. The data member is initialized to
the value of publishers.publisherstitles. You may remember that, we had created
a relation object named publisherstitles. These changes are shown in screen
2.47. As a consequence, with every change in the name of the publisher, the
records retrieved from the titles table shall also vary.
|
Screen 2.47 |
On running the application, a
form window similar to the one shown in screen 2.48 will show up. The screen 2.49 reveals that the publisher 'New Moon
Book' has published 5 books, whereas, the publisher 'Algodata Infosys' has
published 6 books.
|
|
Screen 2.48 |
Screen 2.49 |
The above is an ideal example to demonstrate a parent-child relationship, where every publisher in the listbox, is displayed along with its corresponding titles in the data grid. The important point here is that, the DataMember property of the DataGrid points to the relation object, since it stores the details of the relationship.