C-Sharp Database Basics - North Texas PC User's Group

advertisement
C# Database Basics – An ADO.NET Tutorial
Chris Morgan, .NET SIG of the North Texas PC User Group
April 5, 2014
This tutorial shows how to use basic ADO.NET commands and build an application with an editable grid bound to
an Access or SQL Server database. It is based on the book C# Database Basics by Michael Schmalz, O’Reilly Press,
2012.
The purpose of the application is to display for edit any selected table in a given database. A DataGridView object
will be used to display the data in a Windows Form. The project will have a single form with two grids and a few
buttons. The user will scroll through a list of the tables in a database and select which one he wants to edit. The
selected table will be displayed at the top of the form. The style of development will have a strong emphasis on
writing code rather than setting properties of objects. In summary, here is what we will do to create this app:
1. Create a Window Forms project in Visual Studio and place a few controls on the form, including a
DataGridView. Then, switch to the code view and start adding code to finish the application.
2. Add some class-level variables.
3. Create a DataAdapter. It needs both a connection string and a select query.
4. Create a CommandBuilder object referencing the above DataAdapter.
5. Create a DataTable object.
6. Fill the DataTable using the ‘Fill’ method of the DataAdapter. This one method does the following: opens
a connection , executes the select query, defines and populates the DataTable, and then closes the
connection. A lot is done with this one method!
7. Create a BindingSource object with the DataTable as the DataSource. Then, assign it as the DataSource of
the DataGridView. The DataSource of the DataGridView is the BindingSource, and the DataSource of the
BindingSource is the DataTable.
8. Add all the column names of the table (referenced in the Select query) to a combo box list.
9. Populate a second DataGridView on the form with a list of the “Table” objects in the database.
10. Add code to make the grid editable.
11. Create a button that will filter all the rows in the first grid, based on which cell is highlighted. Create a
second button to clear the filter.
12. Create a button that will switch which table is displayed in the top grid. It will be the table currently
highlighted (selected; clicked-on) in the second grid on the form.
Important classes used in this application:




DataGridView – Two of these will be used. One to display the rows of a selected table. The other will
show a list of all the tables in the database.
DataTable – This object will contain a local, disconnected representation of the selected table from either
SQL Server or Access. It will contain only the columns and rows specified by the select query.
DataAdapter – This class is used to control getting and saving data. It stands between the DataTable and
the source of the data and controls all the traffic between the two.
CommandBuilder – Takes care of creating the CRUD SQL commands used by the DataAdapter.

BindingSource – This class has the commands used for navigation in the DataTable used by the
DataGridView. It sits between the DataTable and the DataGridView.
Steps to perform for the tutorial (yellow highlighting indicates commands that would need to change if using
MS SQL (or, SQL Server Compact Edition) rather than an Access database):
1. Set up Access (or a local SQL Server) running on your computer. Optionally, you can use SQL Server
Compact Edition, if you have it installed.
2. Download and attach the Northwind database in this SQL Server.
http://www.microsoft.com/en-us/download/details.aspx?id=23654
The download for an Access version can be found here:
http://office.microsoft.com/en-us/templates/results.aspx?qu=northwind&ex=1&av=all&AxInstalled=1&c=0
3. Open Visual Studio. Note: Since this tutorial uses pretty basic command, you can use almost any version
of Visual Studio to perform this tutorial. The commands and objects used are based on .NET 2.0+
ADO.NET.
4. File  New Project  Windows Form Application. Give it a name, like “EditingDatabase”.
5. From the toolbox on the left side of the VS screen (If not visible, go to View  Toolbox to show it)
6. Drag the following controls from the Toolbox onto the form:
a. DataGridView, from the Data section. Leave “Source” as none.
b. TextBox, from the Common section
c. ComboBox, from the Common section
d. Two Buttons, from the Common section
e. Another DataGridView – uncheck all the “Enable” checkboxes on the popup. Leave “Source” as
none.
7. Go to “Code View” by pressing <F7>, or <Right-Click> and choose it from the popup menu.
8. Add the following namespace to the list (when using Access file):
using System.Data.OleDb;
//System.Data.SqlClient
9. Add the following class-level variables (i.e., “fields”) to the Form1 class:
public
public
public
public
public
public
string connString;
string query;
OleDbDataAdapter dAdapter;
DataTable dTable;
OleDbCommandBuilder cBuilder;
DataView myDataView;
//SqlDataAdapter
//SqlCommandBuilder
10. In Form1, after the “initialize_component();”, add the following code:
connString = "Provider=Microsoft.ACE.OLEDB.12.0;
Data Source=C:\\users\\chris\\documents\\Northwind.accdb";
query = "SELECT * FROM Products";
dAdapter = new OleDbDataAdapter(query, connString);
//SqlDataAdapter
dTable = new DataTable();
cBuilder = new OleDbCommandBuilder(dAdapter);
//SqlCommandBuilder
cBuilder.QuotePrefix = "[";
cBuilder.QuoteSuffix = "]";
11. Then, after the code just entered, add the following code:
dAdapter.Fill(dTable);
BindingSource bndSource = new BindingSource();
bndSource.DataSource = dTable;
this.dataGridView1.DataSource = bndSource;
for (int q = 0; q <= dataGridView1.ColumnCount - 1; q++)
{
this.comboBox1.Items.Add(this.dataGridView1.Columns[q].HeaderText.ToString());
}
OleDbConnection xyz = new OleDbConnection(connString);
//SqlConnection
xyz.Open();
DataTable tbl = xyz.GetSchema("Tables");
dataGridView2.DataSource = tbl;
myDataView = dTable.DefaultView;
12. Add the following code after the Form1() method:
private void Cell_Update(object sender, DataGridViewCellEventArgs e)
{
try
{
dAdapter.Update(dTable);
this.textBox1.Text = "Updated " + System.DateTime.Now.ToString();
}
catch (OleDbException f)
//SqlException
{
this.textBox1.Text = "Not Updated " + f.Source.ToString();
}
}
13. Then, to “wire up” this code to the grid so that the event of changing rows will call this method to save
any row edits back to the database do the following:
a. Switch back to the Design View on Form1 – press <F7> or <Right-Click> and select “View
Designer”
b. <Right-Click> on the first datagrid and select “Properties”
c. <Click> on the lightning bolt
d. Find the “RowValidated” event and select the “Cell_Update” method; the name of the method we
just added in the previous step.
14. Now we’re going to set up some filtering on the grid. This will allow clicking on a grid cell and show only
those rows which have the same value. To start, change the button on the screen to say “Set Filter” and
“Clear Filter”
a. In Design View, <Right-click> on the button, choose properties, and change the “Text” property of
each button – “Set Filter” for button1 and “Clear Filter for button2.
15. Now, enter the following code below the “Cell_Update” method. It will be called when we click on
button1 to carry out the filtering action:
private void Filter_Click(object sender, EventArgs e)
{
string mystr;
if (myDataView.RowFilter == "")
{
mystr = "[" + dataGridView1.CurrentCell.OwningColumn.HeaderText.ToString() + "]";
mystr += " = '" + dataGridView1.CurrentCell.Value.ToString() + "'";
myDataView.RowFilter = mystr;
// Or, set bndSource.Filter = mystr;, if it was a “class” level variable
}
else
{
mystr = myDataView.RowFilter + " and ";
mystr += "[" + dataGridView1.CurrentCell.OwningColumn.HeaderText.ToString()+"]";
mystr += " = '" + dataGridView1.CurrentCell.Value.ToString() + "'";
myDataView.RowFilter = mystr;
// Or, set the bndSource.Filter = mystr;, if it was a “class” level var
}
}
16. Then, wire up this method to button1 by:
a. Switch back to Design View
b. <Right-Click> on the “Set Filter” button
c. Choose “Properties” and find the “Click” event
d. Choose the method “Filter_Click” from the dropdown for this event
17. Now we’ll create the code to undo the filter. Add the following method after the Filter_Click method:
private void Clear_Filter(object sender, EventArgs e)
{
myDataView.RowFilter = "";
// Or, set bndSource.Filter = ""; if it was a “class” level var
}
18. And wire up the Click event on button2 (Clear Filter) to this method.
The next change is an interesting one. What we have in the second grid is a list of the table objects in the
database. We can use this grid to select which table is displayed in the upper grid with the final button we will
add.
19. Add another button to the form and place it just above the second grid. Right justify it. Change the Text
property to “Change Source”.
20. Add the following code after the last code added above; the Clear_Filter method.
private void Change_Data_Source(object sender, EventArgs e)
{
string tbl_str = dataGridView2.CurrentRow.Cells[2].Value.ToString();
query = "SELECT * FROM [" + tbl_str + "]";
dAdapter = new OleDbDataAdapter(query, connString);
//SqlDataAdapter
dTable = new DataTable();
cBuilder = new OleDbCommandBuilder(dAdapter);
//SqlCommandBuilder
cBuilder.QuotePrefix = "[";
cBuilder.QuoteSuffix = "]";
dAdapter.Fill(dTable);
BindingSource bSource = new BindingSource(); // NOTE: this is a different var name.
bSource.DataSource = dTable; //NOTE: But the DefaultView is what is actually used.
this.dataGridView1.DataSource = bSource;
for (int q = 0; q <= dataGridView1.ColumnCount - 1; q++)
{
this.comboBox1.Items.Add(this.dataGridView1.Columns[q].HeaderText.ToString());
}
myDataView = dTable.DefaultView;
}
Important note: What is not clear about the way this is programmed is that setting the
RowFilter property on the DefaultView (via the ‘myDataView’ variable) actually filters
the rows displayed in the grid. That is, even though the DataTable is what was set as
the DataSource on bSource, filtering the DataView actually results in the filtered set
of rows being displayed. In other words, when binding a DataTable to a grid, it is, in
fact, binding the ‘DefaultView’ of the DataTable to the grid rather than the DataTable
itself. Hence, when we change properties of the DataView we wind up changing the set
of rows (DataViewRows) that are displayed by the grid.
21. Now go back to the button just added and set the Click event to use this new method.
That’s it. If everything was entered correctly you should be able to run the program. It should look like this:
Miscellaneous Notes
1. Using an Access, SQL Server, or SQL Server Compact Edition Database
It is a relatively easy task to switch between different databases in this sample application. The namespace
reference will be one of these: System.Data.SqlClient, System.Data.OleDb, or System.Data.SqlServerCe. Then,
when creating objects that connect to one of these databases, you must create objects in the appropriate
namespace. Here is a table that shows the various class names:
SQL Server
SQL Server Compact Edition
Access
System.Data.SqlClient
System.Data.SqlServerCe
System.Data.OleDb
SqlConnection
SqlCeConnection
OleDbConnection
SqlCommand
SqlCeCommand
OleDbCommand
SqlDataAdapter
SqlCeDataAdapter
OleDbDataAdapter
SqlCommandBuilder
SqlCeCommandBuilder
OleDbCommandBuilder
SqlException
SqlCeException
OleDbException
SqlDataReader
SqlCeDataReader
OleDbDataReader
Note: If the System.Data.SqlServerCe namespace isn’t available in your project, add a “reference” to the DLL in
the Solution Explorer, References section: <Right+Click> on “References”  pick “Add Reference”  click on
“Extensions”  put a check on “System.Data.SqlServerCe”.
2. Using a SqlDataReader:
static void RetrieveMultipleResults(SqlConnection conn)
{
using (conn)
// “using” takes care of opening and closing the connection.
{
SqlCommand command = new SqlCommand(
"SELECT CategoryID, CategoryName FROM dbo.Categories;" +
"SELECT EmployeeID, LastName FROM dbo.Employees",
conn);
conn.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.HasRows)
{
Console.WriteLine("\t{0}\t{1}", reader.GetName(0), reader.GetName(1));
while (reader.Read())
{
Console.WriteLine("\t{0}\t{1}", reader.GetInt32(0), reader.GetString(1));
}
reader.NextResult();
}
}
}
3. LINQ to whatever:
LINQ to DataSet allows developers to create complex, powerful queries over a DataSet by using LINQ. A LINQ to
DataSet query returns an enumeration of DataRow objects, however, which is not easily used in a binding
scenario. DataView can be created from a LINQ to DataSet query and takes on the filtering and sorting
characteristics of that query. LINQ to DataSet extends the functionality of the DataView by providing LINQ
expression-based filtering and sorting, which allows for much more complex and powerful filtering and sorting
operations than string-based filtering and sorting. See Data Binding and LINQ to DataSet for more information.
(http://msdn.microsoft.com/en-us/library/bb552413(v=vs.110).aspx)
4. Entity Framework
More to come here…
Download