执行数据库命令Command对象——ADO.NET学习&应用笔记之三
数据提供程序的Command类是IDBCommand接口的实现,通过Command来执行数据库命令,数据库数据的查询、更新、插入都通过Command来实现。
Command的构造函数通常都有下面三种形式:
1, public xxxCommand();
2, public xxxCommand(string);
3, public xxxCommand(string,xxxConnection);
一般我们创建Command对象都通过类似于下面的语句来实现:
xxxConnection conn = new xxxConnection("myString");
xxxCommand myCmd = new xxxCommand("select * from orders",conn);
构造函数的两个参数是SQL语句和Connection对象,创建了Command对象。
下面说明Command对象的执行,程序范例将采用SqlClient数据提供程序来访问一个Sql Server的数据库Northwind。
一、设置连接和SQL命令
Command类的属性CommandText用来设置命令语句,Connection属性用来设置连接对象。设置命令对象的数据连接和命令语句除了可以在通过创建对象时通过构造函数定义外,这两个属性设置和更改。
SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
conn.Open();
SqlCommand cmd = new SqlCommand("select * from [Orders]",conn);
cmd.CommandText = "delete [Orders] where [OrderID]=10248";
二、执行命令
建立了数据源的连接和设置了命令之后,Command对象执行SQL命令有三种方法:ExecuteNonQuery、ExecuteReader和ExecuteScalar。
使用ExecuteNonQuery执行命令不会返回结果集,只会返回语句影响的记录行数,它适合执行插入、更新、删除之类不返回结果集的命令。如果是SELECT语句,那么返回的结果是-1,如果发生回滚这个结果也是-1。下面的程序范例对Orders表执行了更新并做了查询。
using System;
using System.Data;
using System.Data.SqlClient;
public class myDataAccess{
public static void Main(){
SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
SqlCommand cmd = new SqlCommand("update [Orders] set [OrderDate]='2004-9-1' where [OrderID]=10248",conn);
try{
conn.Open();
int i = cmd.ExecuteNonQuery();
Console.WriteLine(i.ToString() + " rows affected by UPDATE");
cmd.CommandText = "select * from [Orders]";
i = cmd.ExecuteNonQuery();
Console.WriteLine(i.ToString() + " rows affected by SELECT");
}
catch(Exception ex){
Console.WriteLine(ex.Message);
}
finally{
conn.Close();
}
}
}
使用ExecuteReader方法执行的命令,可以返回一个类型化的DataReader实例或者IDataReader接口的结果集。通过DataReader对象就能够获得数据的行集合,本文不准备详细讨论DataReader,关于DataReader的使用将来再说明。下面是一个例子。
using System;
using System.Data;
using System.Data.SqlClient;
public class myDataAccess{
public static void Main(){
SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
SqlCommand cmd = new SqlCommand("select top 20 * from [Orders]",conn);
SqlDataReader reader; //或者IDataReader reader;
try{
conn.Open();
reader = cmd.ExecuteReader();
while(reader.Read()){
Console.WriteLine(reader[0].ToString());
}
reader.Close();
}
catch(Exception ex){
Console.WriteLine(ex.Message);
}
finally{
conn.Close();
}
}
}
对于ExecuteReader方法,如果想获得数据的记录行数,可以通过select count(*)这样的语句取得一个聚合的行集合。对于这样求单个值的语句,Command对象还有更有效率的方法——ExecuteScalar。它能够返回对应于第一行第一列的对象(System.Object),通常使用它来求聚合查询结果。需要注意的是,如果需要把返回结果转化成精确的类型,数据库在查询中就必须强制将返回的结果转换,否则引发异常。下面是例子:
using System;
using System.Data;
using System.Data.SqlClient;
public class myDataAccess{
public static void Main(){
SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
SqlCommand cmd = new SqlCommand("select count(*) from [Orders]",conn);
try{
conn.Open();
int i = (int)cmd.ExecuteScalar();
Console.WriteLine("record num : " + i.ToString());
cmd.CommandText = "select cast(avg([Freight]) as int) from [Orders]";
int avg = (int)cmd.ExecuteScalar();
Console.WriteLine("avg : " + avg.ToString());
cmd.CommandText = "select avg([Freight]) from [Orders]";
avg = (int)cmd.ExecuteScalar(); //引发异常
Console.WriteLine("avg : " + avg.ToString());
}
catch(Exception ex){
Console.WriteLine(ex.Message);
}
finally{
conn.Close();
}
}
}
这个程序中,最后一个查询将引发异常,因为聚合返回的结果是float类型的,无法转换。
三、参数化查询
参数化的查询能够对性能有一定的优化,因为带参数的SQL语句只需要被SQL执行引擎分析过一次。Command的Parameters能够为参数化查询设置参数值。Parameters是一个实现IDataParamterCollection接口的参数集合。
不同的数据提供程序的Command对参数传递的使用不太一样,其中SqlClient和OracleClient只支持SQL语句中命名参数而不支持问号占位符,必须使用命名参数,而OleDb和Odbc数据提供程序只支持问号占位符,不支持命名参数。
对于查询语句SqlClient必须使用命名参数,类似于下面的写法:
SELECT * FROM Customers WHERE CustomerID = @CustomerID --Oracle的命名参数前面不用@,使用(:),写为(:CustomerID)
而对于OleDb或者Odbc必须使用?占位符,类似于下面的写法:
SELECT * FROM Customers WHERE CustomerID = ?
下面以Sql Server为范例,说明其使用方法:
using System;
using System.Data;
using System.Data.SqlClient;
public class myDataAccess{
public static void Main(String[] args){
SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
SqlCommand cmd = new SqlCommand("select * from [Orders] where [OrderID]=@oid",conn);
SqlDataReader reader;
try{
int param = Convert.ToInt32(args[0]);
cmd.Parameters.Add("@oid",param); //使用命名参数
cmd.Parameters[0].Direction = ParameterDirection.Input;
conn.Open();
reader = cmd.ExecuteReader();
while(reader.Read()){
Console.WriteLine(reader[0].ToString());
}
reader.Close();
}
catch(Exception ex){
Console.WriteLine(ex.Message);
}
finally{
conn.Close();
}
}
}
对于OleDb或者Odbc数据提供程序的命令参数,只需要把参数按照占位符从左到右的顺序,匹配给Parameters集合就行了。 下面是程序范例:
using System;
using System.Data;
using System.Data.OleDb;
public class myDataAccess{
public static void Main(String[] args){
OleDbConnection conn = new OleDbConnection("Provider=SQLOLEDB;Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
OleDbCommand cmd = new OleDbCommand("select * from [Orders] where [OrderID]=? or [EmployeeID]=?",conn);
OleDbDataReader reader;
try{
int param1 = Convert.ToInt32(args[0]);
int param2 = Convert.ToInt32(args[1]);
cmd.Parameters.Add("aaa",param1);
cmd.Parameters.Add("bbb",param2); //参数对象还需要名字,但是和查询语句中的参数名无关
cmd.Parameters[0].Direction = ParameterDirection.Input;
cmd.Parameters[1].Direction = ParameterDirection.Input;
conn.Open();
reader = cmd.ExecuteReader();
while(reader.Read()){
Console.WriteLine(reader[0].ToString());
}
reader.Close();
}
catch(Exception ex){
Console.WriteLine(ex.Message);
}
finally{
conn.Close();
}
}
}
四、执行存储过程
使用Command对象访问数据库的存储过程,需要指定CommandType属性,这是一个CommandType枚举类型,默认情况下CommandType表示CommandText命令为SQL批处理,CommandType.StoredProcedure值指定执行的命令是存储过程。类似于参数化查询,存储过程的参数也可以使用Parameters集合来设置,其中Parameter对象的Direction属性用于指示参数是只可输入、只可输出、双向还是存储过程返回值参数。
需要注意的是如果使用ExecuteReader返回存储过程的结果集,那么除非DataReader关闭,否则无法使用输出参数。下面是一个例子:
存储过程
---------------------------------------------
CREATE procedure myProTest (
@orderID as int,
@elyTitle as varchar(50) output
)
as
select @elyTitle=ely.Title from [Orders] o join [Employees] ely on ely.EmployeeID=o.EmployeeID where o.OrderID=@orderID
select * from [Orders] where OrderID=@orderID
return 1
---------------------------------------------
程序
---------------------------------------------
using System;
using System.Data;
using System.Data.SqlClient;
public class myDataAccess{
public static void Main(){
SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
SqlCommand cmd = new SqlCommand("myProTest",conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@orderID",10252);
cmd.Parameters.Add("@elyTitle",SqlDbType.VarChar,50);
cmd.Parameters.Add("@return",SqlDbType.Int);
cmd.Parameters[0].Direction = ParameterDirection.Input;
cmd.Parameters[1].Direction = ParameterDirection.Output;
cmd.Parameters[2].Direction = ParameterDirection.ReturnValue;
SqlDataReader reader;
try{
conn.Open();
Console.WriteLine("execute reader...");
reader = cmd.ExecuteReader();
Console.WriteLine("@orderID = {0}",cmd.Parameters[0].Value);
Console.WriteLine("@elyTitle = {0}",cmd.Parameters[1].Value);
Console.WriteLine("Return = {0}",cmd.Parameters[2].Value);
Console.WriteLine("reader close...");
reader.Close();
Console.WriteLine("@orderID = {0}",cmd.Parameters[0].Value);
Console.WriteLine("@elyTitle = {0}",cmd.Parameters[1].Value);
Console.WriteLine("Return = {0}",cmd.Parameters[2].Value);
Console.WriteLine("execute none query...");
cmd.ExecuteNonQuery();
Console.WriteLine("@orderID = {0}",cmd.Parameters[0].Value);
Console.WriteLine("@elyTitle = {0}",cmd.Parameters[1].Value);
Console.WriteLine("Return = {0}",cmd.Parameters[2].Value);
}
catch(Exception ex){
Console.WriteLine(ex.Message);
}
finally{
conn.Close();
}
}
}
和参数化查询一样,OleDb或者Odbc数据提供程序不支持存储过程的命名参数,需要把参数按照从左到右的顺序,匹配Parameters集合。