Post on 23-Dec-2015
Lecture 11:
Programmatic Database Access with ADO.NET
11-2MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Objectives
“Programmatic database access typically involves executing SQL queries using classes from the language's framework. In .NET, the Active Data Objects (ADO) classes in the FCL are the primary means of database programming. ADO.NET is a vendor-neutral, object-oriented, SQL-based approach…”
• Architecture of ADO.NET• Basic database access• Application design• Updating a database• DataSets
11-3MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Part 1
• Architecture of ADO.NET…
11-4MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Relational technology
• ADO.NET is designed to access relational databases• Example:
– Sales database with customers, orders, and products
11-5MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Architecture
• ADO.NET architecture based on data providers
– data providers encapsulate DB-specific details
Data Provider
ADO.NET
.NET Programmer
DB
11-6MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Existing data providers
• .NET currently ships with 4 data providers:– one for Microsoft SQL Server– one for Oracle– one for older OLEDB technology (used for ADO, VB6)– one for older ODBC (Open Database Connectivity) technology
• More third-party providers are available…– Oracle's own provider: http://otn.oracle.com/tech/windows/odpnet/
– DB2: http://www7b.software.ibm.com/dmdd/downloads/dotnetbeta/
– MySQL: http://www.mysql.com/, http://crlab.com/mysqlnet/
– etc.
11-7MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
ADO.NET object model
• ADO.NET is an object-oriented approach• Classes are spread across a number of FCL namespaces
– some are provider-neutral, others are provider-specific
SQL Server
System.Data.Common
System.Data
System.Data.SqlClient
System.Data.OleDb
other DBs, e.g. MS Access
provider-neutral
ODBC (Open Database Connectivity)
System.Data.Odbc
11-8MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Part 2
• Basic database access…
11-9MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Overview of database access
• Three steps:
1. open connection to database
2. execute SQL to retrieve records / update DB
3. close connection
11-10MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
import System.Data.*;import System.Data.OleDb.*;
String sConnection;sConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=Sales.mdb";
OleDbConnection dbConn;dbConn = new OleDbConnection(sConnection);dbConn.Open();
MessageBox.Show(dbConn.get_State().toString());
import System.Data.*;import System.Data.OleDb.*;
String sConnection;sConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=Sales.mdb";
OleDbConnection dbConn;dbConn = new OleDbConnection(sConnection);dbConn.Open();
MessageBox.Show(dbConn.get_State().toString());
(1) Open connection
• Connections are opened based on connection string info– here we open a connection to a MS Access 2000 database– "Sales.mdb" must exist in same dir as .EXE (e.g. bin\Debug)
connection
11-11MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Building connection strings
• Connection strings are vendor-specific• Connection strings are not well-documented• Where to turn for help?
– www.connectionstrings.com– www.able-consulting.com/ADO_conn.htm
11-12MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
(2) Retrieve records
• Retrieve records via SQL Select query– read-only access via DataReader & field names
String sql, fn, ln; Customer c;sql = "Select * From Customers Order By LastName Asc, FirstName Asc;";
OleDbCommand dbCmd;OleDbDataReader dbReader;
dbCmd = new OleDbCommand(sql, dbConn);dbReader = dbCmd.ExecuteReader();
while ( dbReader.Read() ) // retrieve records one-by-one… {
fn = String.valueOf(dbReader.get_Item("FirstName")); ln = String.valueOf(dbReader.get_Item("LastName")); c = new Customer(fn, ln); this.listBox1.get_Items().Add(c);
}
String sql, fn, ln; Customer c;sql = "Select * From Customers Order By LastName Asc, FirstName Asc;";
OleDbCommand dbCmd;OleDbDataReader dbReader;
dbCmd = new OleDbCommand(sql, dbConn);dbReader = dbCmd.ExecuteReader();
while ( dbReader.Read() ) // retrieve records one-by-one… {
fn = String.valueOf(dbReader.get_Item("FirstName")); ln = String.valueOf(dbReader.get_Item("LastName")); c = new Customer(fn, ln); this.listBox1.get_Items().Add(c);
}
data reader recordrecordrecord
field name of data value in current record
command connection
11-13MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
(3) Close connection
• Be sure to close reader and connection…– to flush pending updates (in general)– so others can access DB (connections are limited resources)
dbReader.Close();dbConn.Close();dbReader.Close();dbConn.Close();
11-14MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Part 3
• Data-driven application design…
11-15MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Data Tier design
• Recall N-Tier design… GUI.exe DataAccess
import System.Data.*;import System.Data.OleDb.*;
public class DataAccess{ private String sConnection;
public DataAccess(String filename) { this.sConnection = String.Format("Provider=...;Data Source={0}{1}", System.AppDomain.get_CurrentDomain().get_BaseDirectory(), filename); }
public java.util.ArrayList getCustomers() throws System.Exception { . . . }
import System.Data.*;import System.Data.OleDb.*;
public class DataAccess{ private String sConnection;
public DataAccess(String filename) { this.sConnection = String.Format("Provider=...;Data Source={0}{1}", System.AppDomain.get_CurrentDomain().get_BaseDirectory(), filename); }
public java.util.ArrayList getCustomers() throws System.Exception { . . . }
11-16MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Guaranteed close?
• Ensure reader / DB closed via try-catch-finally pattern– and *note* that we want to catch .NET System.Exception objects, not
Java java.lang.Exception objects…
OleDbConnection dbConn = null;OleDbDataReader dbReader = null;
try { dbConn = new OleDbConnection(…); dbConn.Open(); . . .}catch(System.Exception ex) { System.Diagnostics.Debug.WriteLine("DB error: " + ex.get_Message()); throw new System.Exception("Data Error!", ex);}finally { // always executes whether we succeed or throw exception… if (dbReader != null) dbReader.Close(); if (dbConn != null) dbConn.Close();}
OleDbConnection dbConn = null;OleDbDataReader dbReader = null;
try { dbConn = new OleDbConnection(…); dbConn.Open(); . . .}catch(System.Exception ex) { System.Diagnostics.Debug.WriteLine("DB error: " + ex.get_Message()); throw new System.Exception("Data Error!", ex);}finally { // always executes whether we succeed or throw exception… if (dbReader != null) dbReader.Close(); if (dbConn != null) dbConn.Close();}
11-17MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Part 4
• Updating a database…
11-18MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Executing action queries
• Use action queries when you need to modify a DB– updates– inserts– deletes
• Execute action queries via ExecuteNonQuery method…
11-19MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Basic idea
• Open, execute, and close:
String sConnection, sql;sConnection = "...";sql = "...";
OleDbConnection dbConn; OleDbCommand dbCmd;dbConn = new OleDbConnection(sConnection);dbCmd = new OleDbCommand(sql, dbConn);
int rows;dbConn.Open(); rows = dbCmd.ExecuteNonQuery(); // returns # of rows affected…dbConn.Close();
if (rows != 1) // sanity check to make sure it worked... throw new System.Exception("Query ran but failed to update DB?!");
String sConnection, sql;sConnection = "...";sql = "...";
OleDbConnection dbConn; OleDbCommand dbCmd;dbConn = new OleDbConnection(sConnection);dbCmd = new OleDbCommand(sql, dbConn);
int rows;dbConn.Open(); rows = dbCmd.ExecuteNonQuery(); // returns # of rows affected…dbConn.Close();
if (rows != 1) // sanity check to make sure it worked... throw new System.Exception("Query ran but failed to update DB?!");
11-20MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Dynamic SQL
• Most of the time you'll need to build SQL dynamically– i.e. based on input values from the user
• Example:– delete the selected customer…
String fn, ln; Customer c;
c = this.listBox1.get_SelectedItem();fn = c.firstName;ln = c.lastName;
sql = "...";
String fn, ln; Customer c;
c = this.listBox1.get_SelectedItem();fn = c.firstName;ln = c.lastName;
sql = "...";
11-21MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Dynamic string building
• Dynamic SQL requires dynamic string building• Example:
– build dynamic SQL to delete selected customer…
– don't forget the delimiters for strings & dates!
String sql;
sql = String.Format("Delete From Customers Where " + "FirstName='{0}' And LastName='{1}';", fn, ln);
String sql;
sql = String.Format("Delete From Customers Where " + "FirstName='{0}' And LastName='{1}';", fn, ln);
11-22MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Example
• Delete selected customer…
private void listBox1_SelectedIndexChanged(…){ String fn, ln, sql; int rows; Customer c; c = (Customer) this.listBox1.get_SelectedItem(); if (c == null) return; // nothing selected…
fn = c.firstName; ln = c.lastName; sql = String.Format("Delete From Customers Where " + "FirstName='{0}' And LastName='{1}';", fn, ln); . . . dbConn.Open(); rows = dbCmd.ExecuteNonQuery(); // delete! dbConn.Close();
if (rows != 1) throw new System.Exception("Query ran but failed to delete?!");
this.listBox1.get_Items().Remove(c); // update GUI! MessageBox.Show("Deleted!");}
private void listBox1_SelectedIndexChanged(…){ String fn, ln, sql; int rows; Customer c; c = (Customer) this.listBox1.get_SelectedItem(); if (c == null) return; // nothing selected…
fn = c.firstName; ln = c.lastName; sql = String.Format("Delete From Customers Where " + "FirstName='{0}' And LastName='{1}';", fn, ln); . . . dbConn.Open(); rows = dbCmd.ExecuteNonQuery(); // delete! dbConn.Close();
if (rows != 1) throw new System.Exception("Query ran but failed to delete?!");
this.listBox1.get_Items().Remove(c); // update GUI! MessageBox.Show("Deleted!");}
11-23MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Oops!
• Try to delete "O'Dahl, Kathie"…• What happens?
11-24MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Escaping delimiters
• Most common DB programming mistake:– forgetting to escape delimiter characters…
• Solution?– just replace ' with 2 in a row, i.e. ' '
private void listBox1_SelectedIndexChanged(…){ String fn, ln, sql; int rows; Customer c; c = (Customer) this.listBox1.get_SelectedItem(); if (c == null) return; // nothing selected…
fn = c.firstName; ln = c.lastName;
fn = fn.Replace("'", "''"); ln = ln.Replace("'", "''"); sql = String.Format("Delete From Customers Where " + "FirstName='{0}' And LastName='{1}';", fn, ln);
private void listBox1_SelectedIndexChanged(…){ String fn, ln, sql; int rows; Customer c; c = (Customer) this.listBox1.get_SelectedItem(); if (c == null) return; // nothing selected…
fn = c.firstName; ln = c.lastName;
fn = fn.Replace("'", "''"); ln = ln.Replace("'", "''"); sql = String.Format("Delete From Customers Where " + "FirstName='{0}' And LastName='{1}';", fn, ln);
11-25MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Part 5
• DataSets…
11-26MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
DataSets
• DataSets are an in-memory data structure– easily filled with data from a database– easily passed around– easily displayed in a GUI app
DataSet
Name Price Stock
Ants $ 0.49 5000
Birds $ 4.49 500
Cats $29.95 100
Dogs $79.95 20
DBCommand ConnectionDataAdapter
• DataSet mirrors the database– data forms a temporary table called
"Table" within DataSet"Table"
11-27MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Filling a DataSet
• DataAdapter object is used to fill a DataSet…• Example:
– fill DataSet with all product data
sql = "Select * From Products Order By Name Asc;"; . . .DataSet ds;OleDbDataAdapter adapter;ds = new DataSet();adapter = new OleDbDataAdapter(dbCmd);
dbConn.Open(); adapter.Fill(ds);dbConn.Close();
sql = "Select * From Products Order By Name Asc;"; . . .DataSet ds;OleDbDataAdapter adapter;ds = new DataSet();adapter = new OleDbDataAdapter(dbCmd);
dbConn.Open(); adapter.Fill(ds);dbConn.Close();
"Table"
Name Price Stock
Ants $ 0.49 5000
Birds $ 4.49 500
Cats $29.95 100
Dogs $79.95 20
11-28MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
DataGrid display
• DataSet can be bound to DataGrid control for easy display– one line of code!
. . .this.dataGrid1.SetDataBinding(ds, "Table");
. . .this.dataGrid1.SetDataBinding(ds, "Table");
11-29MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
DataGrid is a powerful control
• By default, DataGrid is read/write– user can modify data– user can add rows– user can delete rows
• However, all changes are local to DataSet– to flush changes back to DB, reconnect and update…
11-30MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Flushing changes back to database
• Reconnect, and apply adapter's Update() method– use CommandBuilder object to generate necessary SQL for you
// retrieve existing data set from grid…ds = (DataSet) this.dataGrid1.get_DataSource();
. . .
OleDbCommandBuilder cmdBuilder;cmdBuilder = new OleDbCommandBuilder(adapter);
dbConn.Open(); adapter.Update(ds); // this will throw exception if update(s) conflict… dbConn.Close();
// retrieve existing data set from grid…ds = (DataSet) this.dataGrid1.get_DataSource();
. . .
OleDbCommandBuilder cmdBuilder;cmdBuilder = new OleDbCommandBuilder(adapter);
dbConn.Open(); adapter.Update(ds); // this will throw exception if update(s) conflict… dbConn.Close();
11-31MicrosoftIntroducing CS using .NETJ# in Visual Studio .NET
Summary
• Databases are a critical component of most business apps• SQL is the standard programming language for databases
• Database programming is based on framework classes– in .NET, those classes are called ADO.NET– the more you know about SQL the better