Databases with C#.NET
Though you are likely to be familiar with SQL (Standard Query Language), we provide a short introduction.
Here is an example of a database, which was created with Microsoft Access.
It was stored in a file named MusicSales.mdb
. It
consists of two tables: the first was named Artists
, and the second was
named Companies
. Incidentally, we could have created the database by
writing C# code, but using Access is simpler.
Here is the Artists
table, involving recording artists, their management
company, and their music sales (in millions of dollars):
Artist | Company | Sales |
The Visuals | ABC Co | 65.2 |
RadioStar | ABC Co | 22.7 |
Mike Parr | Media Ltd | 3.5 |
The Objects | Class UK | 12.6 |
Assignment 182 | Media Ltd | 34.6 |
The Trees | United Co | 3.72 |
Companies
(with Company
set as the primary key for the
table) , which lists company names together with their location, as shown
here:
Company | Location |
ABC Co | London |
Media Ltd | Chicago |
Class UK | Madrid |
United Co | London |
Company
column of the
Artists
table is a foreign key of The Company
column in the Companies
table. This was specified when we designed the database.
Before we do this, here are some examples of the most useful SQL statements. In traditional use, SQL statements must be terminated with a semicolon, but this is not needed when we pass an individual statement to the ADO.NET classes.
select
statement
select
and insert
), but capitals can also be used.
select Artist from ArtistsThis returns an item from every
Artist
field in the Artists
table (i.e.
the complete Artist column).
select * from ArtistsThis returns the items from every field in the
Artists
table.
Effectively, it returns the complete table.
select * from Artists where Company = 'Media Ltd'returns only the records from
Artists
where the company is Media
Ltd
. We enclose strings in single quotes. Strings are case-
sensitive, so use capitals where they were used in the original database.
select * from Artists where Sales > 20Returns only those records with sales above 20. Note that there are no quotes around numbers. The available operators are
> < >= <= <> =where
<>
means 'not equals'.
select * from Companies where Company = 'ABC Co'This returns the single record from
Companies
, involving ABC
Co
select * from Artists order by Sales ascThe order by item can be added to any selection. We can use
asc
or
desc
to specify ascending or descending order. The above statement puts
the records in ascending order of sales.
insert
statement
insert into Artists (Artist, Company, Sales) values('The Regulars', 'ABC Co', 23.8)Inserts a new record with the specified values. We provide a bracketed list of column names, and a bracketed list of values. Note that we can choose to split a long statement into several lines to improve its readability.
delete
statement
delete from Artists where Artist = 'RadioStar' delete from Artists where Company = 'ABC Co'Deletes the specified record(s).
update
statement
update Artists set Sales = 30.8 where Artist = 'RadioStar' update Artists set Company = 'Class UK', Sales = 56.8 where Artist = 'RadioStar'Updates the
RadioStar
record. The examples show firstly a single field
being updated, then two fields. As with delete
, it is possible to specify
several records in the where
item.
This concludes our SQL overview. There are more statements, and even those we illustrated have extensions. However, the above will suffice for the programs in this chapter.
Directory
are a kind of API, in that they enable programs
to access to the Windows file system.
The ADO.NET classes we will discuss are:
OleDbConnection
, OleDbDataAdapter
, DataTable
, DataGrid
, and
OleDbCommandBuilder
.
The namespaces for these classes are automatically imported into your program.
You might wish to read this section whilst looking at the complete examples, whose screen shots are shown in figures 25.1 and 25.2.
OleDbConnection
class
Open
and Close
methods to manipulate the
connection.
OleDbDataAdapter
class
DataTable
objects
held temporarily in RAM.
To use the class, we drop an instance on our form, then work through the steps provided by a wizard (explained below). The instance is then ready for use.
The class structure of OleDbDataAdapter
is rather complicated: it has some
properties (named SelectCommand
, InsertCommand
, and DeleteCommand
)
which in their turn are instances of another complex class named
OleDbCommand
. In its turn, this class has its own properties and methods
(such as CommandText
and ExecuteNonQuery
). However, because you will not
use the OleDbCommand
class separately, we will present the facilities as if
they all belonged to OleDbDataAdapter
.
The main methods and properties are:
CommandText
oleDbDataAdapter1.SelectCommand.CommandText = "select * from Artists where Company ='ABC Co'";(Recall that the two '.' symbols are needed because we are using the property of a property.)
Fill
CommandText
) into a DataTable
held in RAM. The returned integer provides
a count of the number of matching records found. We can then access the data
table by row and column position. Here is an example of Fill
, showing
CommandText
in use:
DataTable table = new DataTable(); int recordCount; recordCount = oleDbDataAdapter1.Fill(table);
The Fill
method automatically opens the connection if it needs to. This is
not the case with other methods, where we need to explicitly open and close
the connection.
ExecuteNonQuery
UpdateCommand
oleDbDataAdapter1.UpdateCommand.CommandText = "update Artists set Sales = 30.8 where Artist = 'RadioStar'"; oleDbDataAdapter1.UpdateCommand.ExecuteNonQuery();
DeleteCommand
oleDbDataAdapter1.DeleteCommand.CommandText = "delete from Artists where Artist = 'RadioStar'"; oleDbDataAdapter1.DeleteCommand.ExecuteNonQuery();
InsertCommand
oleDbDataAdapter1.InsertCommand.CommandText = "insert into Artists(Artist, Company, Sales) " + "values ('The Famous Five', 'Class UK', 0)"; oleDbDataAdapter1.InsertCommand.ExecuteNonQuery();In our particular
MusicSales
database, an exception will be thrown if we
try to insert a record into Artists with a company that does not
already exist in Companies.
DataTable
class
We can choose to fill a data table with an exact copy of a table in a
database, or we can pick out the records we need. This is determined by the
SQL string we place in CommandText
. Here is an example:
DataTable table = new DataTable();
int recordCount;
recordCount = oleDbDataAdapter1.Fill(table);
We can access individual items of a data table by using the Rows
property,
followed by a row number and column number, as in:
string name;
name = Convert.ToString(table.Rows[0][1]);
Note that:
0
upwards.
Object
, so when we extract an
item, we convert it to the appropriate type. (For example we convert an item
to a String
if it is to be displayed in a text box.)
Count
property, as in:
int lastRowNumber; lastRowNumber = table.Rows.Count - 1;Remember that the rows are numbered from
0
upwards.
To empty a data table, we use the Clear
method.
The row/column structure will be familiar to you from using arrays, and it is tempting to begin to write loops to search data tables. However, this is to be avoided. Try to do as much work as possible in SQL. This approach results in simpler code.
DataGrid
class
DataTable
in row/column format.
It also allows the editing of existing data, and the insertion of new records
at the bottom of the grid. Note that changes to the data are not
automatically transferred to the database. We make use of an
OleDbCommandBuilder
to perform the updating.
We use the DataSource
property to 'bind' a data grid to a data table.
From then on, any changes to the table are automatically displayed in the
grid.
The data grid is in the toolbox, and is positioned in the same way as most controls.
OleDbCommandBuilder
class
DataGrid
program shows it in use.
Now we place a data adapter on the form:
Figure 25.1 shows the form, and here is the code:
private void deleteButton_Click(object sender, System.EventArgs e)
{
try
{
oleDbConnection1.Open();
string command;
//set up an SQL delete
command = "delete from Artists where Artist = '"
+ artistBox.Text + "';";
oleDbDataAdapter1.DeleteCommand.CommandText = command;
sqlLabel.Text = command;
//do the delete
oleDbDataAdapter1.DeleteCommand.ExecuteNonQuery();
}
catch (Exception exceptionObject)
{
MessageBox.Show(exceptionObject.Message);
}
finally
{
oleDbConnection1.Close();
}
}
private void updateButton_Click(object sender, System.EventArgs e)
{
try
{
oleDbConnection1.Open();
string command;
//set up an SQL update
command = "update Artists set Company = '" +
companyBox.Text + "', " + "Sales = " + salesBox.Text
+ " where Artist = '" + artistBox.Text + "'";
oleDbDataAdapter1.UpdateCommand.CommandText = command;
sqlLabel.Text = command;
//do the update
oleDbDataAdapter1.UpdateCommand.ExecuteNonQuery();
}
catch (Exception exceptionObject)
{
MessageBox.Show(exceptionObject.Message);
}
finally
{
oleDbConnection1.Close();
}
}
private void insertButton_Click(object sender, System.EventArgs e)
{
try
{
oleDbConnection1.Open();
string command;
//set up an SQL insert
command = "insert into Artists(Artist, Company, Sales )"
+ " values('" + artistBox.Text + "', '"
+ companyBox.Text + "', " + salesBox.Text + ")";
oleDbDataAdapter1.InsertCommand.CommandText = command;
sqlLabel.Text = command;
//do the insert
oleDbDataAdapter1.InsertCommand.ExecuteNonQuery();
}
catch (Exception exceptionObject)
{
MessageBox.Show(exceptionObject.Message);
}
finally
{
oleDbConnection1.Close();
}
}
private void findButton_Click(object sender, System.EventArgs e)
{
try
{
DataTable table = new DataTable();
string command;
int recordCount;
//set up an SQL query
command = "select * from Artists where "
+ "Artist = " + " '" + artistBox.Text + "'";
oleDbDataAdapter1.SelectCommand.CommandText = command;
sqlLabel.Text = command;
//do the query
table.Clear();
recordCount = oleDbDataAdapter1.Fill(table);
//display any result
if (recordCount != 0)
{
companyBox.Text = Convert.ToString(table.Rows[0] [1]);
salesBox.Text = Convert.ToString(table.Rows[0][2]);
}
else
{
MessageBox.Show("Artist not found!!");
}
}
catch (Exception exceptionObject)
{
MessageBox.Show(exceptionObject.Message);
}
}
Here are some points on the program:
artistBox
, companyBox
, and salesBox
.
SQLLabel
at the
bottom of the form, before executing the command. The reason is that,
although the C# compiler makes detailed checks on your C# coding, it does not
check the contents of strings, and knows nothing about SQL. Thus, SQL
statement errors are only detected at run-time, and it is useful to have a
display of the SQL code. Many errors are simple mistakes with single quotes.
Message
property, which provides an explanation of any errors.
Close
call within the finally
of the
exception-handling code, so that it is performed whatever happens within the
try
.
b). inserting a record?
The user enters a sales figure, and an SQL query is constructed which fills
a data table with records whose sales are equal to or above the entered
value.
A value of 0
will select every record. The data grid can be edited, and
changes can be made permanent via a Save button.
private DataTable table = new DataTable(); //at the top of the code
private void saveButton_Click(object sender, System.EventArgs e)
{
try
{ //NB************* diff from vb- see usings
OleDbCommandBuilder commandbuilder = new
OleDbCommandBuilder(oleDbDataAdapter1);
oleDbDataAdapter1.Update(table);
}
catch (Exception exceptionObject)
{
MessageBox.Show(exceptionObject.Message);
}
}
private void highSalesButton_Click(object sender,
System.EventArgs e)
{
try
{
dataGrid1.DataSource = table;
//set up an SQL Query
oleDbDataAdapter1.SelectCommand.CommandText =
"select * from Artists where "
+ "Sales >= " + salesAboveBox.Text;
//fill data table with results from query
table.Clear();
oleDbDataAdapter1.Fill(table);
}
catch (Exception exceptionObject)
{
MessageBox.Show(exceptionObject.Message);
}
}
Note:
Private
variable, because it is shared by
both methods.
DataSource
property of the grid is used to bind table
. This means
that when table
changes (via the Fill
method) then the table is re-
displayed in the grid automatically
*
.
Fill
, you do not need to use Open
and
Close
. In every other case, they are needed.
Rows
property of a data table takes two bracketed integer expressions,
and returns an Object
. We convert it to our required type, as in:
int n = Convert.ToInt32(table.Rows[3)] [4]);
OleDbConnection OleDbDataAdapter DataTable DataGrid OleDbCommandBuilder
2. Write a program which displays the number of companies in the database.
3. Write a program which displays the highest sales figure, along with the
name of the artist. (Reminder: SQL has an order
by
facility.)
4. Write a program which displays the Artists table in a data grid, in ascending order of sales.
5. Modify the Database Example program so that the insert operation firstly checks that the entered company exists in the Companies table. If it does not, ask the user if they wish to add a new company, and insert a new record into Companies if required.
25.1 ANSWER:
a). The Artist field needs a value. In the program, any values in other text
boxes are ignored. We could enforce this with an if
as follows:
if (artistBox.Text != "") { ...code to delete a record }
b). All three textboxes need values.
25.2 ANSWER:
The coding for the button would be almost identical to the Sales Above
button, but the SQL command can be simplified to:
command = "select * from Artists"
25.3 ANSWER:
The program is essentially the same as the data grid example. The only
difference is the setting-up of the CommandText
property. Here, we provide
a text box to allow the user to enter a query, and we transfer its text to
CommandText
by
oleDbDataAdapter1.SelectCommand.CommandText = commandBox.Text;