2010年7月18日记
ADO.NET架构
1、使用ADO.NET连接最佳练习
2、使用ADO.NET命令最佳练习
3、使用DataReader、DataSet、DataAdapter
4、使用ADO.NET的其他技巧
ADO.NET架构
1、Data Provider
SQL Server Provider
SqlConnection/SqlCommand/SqlDataAdapter/SqlDataReader
OleDbProvider
OleDbConnection/OleDbCommand/OleDbDataAdapter/OleDbDataReader
2、DataSet
DataTable
DataColumn
DataRow
Constraints
DataRelation
3、Data use application
Windows Form
Web Form
Others
1、数据提供程序:
SQL Server .NET数据提供程序
OLE DB.NET数据提供程序
ODBC .NET数据提供程序
基于Oracle的.NET数据提供程序
2、对象概述
Connection类:在代码和容纳数据的数据存储之间提供了基本的连接
Command类:用于描述SQL语句或者通过其Connection类执行的存储过程
DataReader类:从数据源中获取只读的数据流
DataAdapter类:功能最强大、更复杂的对象,可以读取、改变数据源
ADO.NET连接最佳练习
1、使用连接池
用DataAdapter优化连接
始终关闭Connection和DataReader
在C#中使用“Using”语句
异常处理
安全连接
2、为什么使用连接池?
创建连接所发费的时间与资源并不是无价值的
Connection pools可以使在特定页面运行过后,连接能够保持下来
3、使用连接池和不使用连接池的对比
(1、使用连接池:在连接池中,判断是否已有连接?
没有连接,那么创建数据连接,然后向数据库发送查询请求,将返回的数据显示,最后断开连接
如果连接池中已有连接,那么直接向数据库发送查询请求,将返回的数据显示,最后断开连接
(2、不使用连接池:直接创建数据连接,然后向数据库发送查询请求,将返回的数据显示,最后断开连接
显然,连接池减少了数据连接上的性能消耗,效率肯定比不使用连接池高
4、ADO.NET中的连接池
如果使用的是OleDbConnection类,则连接池将由提供程序自动处理,而不必自己进行管理。
如果使用的是SqlConnection类,则连接池被隐式管理,但也提供选项允许自己管理连接池。
在连接池字符串中指定:
Pooling=true;//默认为true
Connection lifetime=5; //默认为0
Min pool size=1; //默认为0
Max pool size=50; //默认为100
用DataAdapter优化连接
1、DataAdapter的Fill和Upadate方法在连接关闭的情况下自动打开为相关命令属性指定的连接。如果Fill或者Update方法打开了连接,Fill或者Update将在操作完成的时候关闭它。为了获得最佳性能,仅在需要时将与数据库的连接保持为打开,同时,减少打开和关闭多操作连接的次数。
2、如果只执行单个的Fill获Update方法调用,建议允许Fill获Update方法隐式打开和关闭连接。如果对Fill和/或Update调用很多,建议显示打开连接,调用Fill和/或Update,然后显示关闭连接。
3、始终关闭Connection和DataReader
完成对Connection或DataReader对象的使用后,总是显示地关闭它们。尽管垃圾回收最终会清除对象并释放连接和其他委托资源,但垃圾回收仅在需要时执行。
4、在C#中是偶那个“Using”语句
Using语句在离开自己的作用范围时,会自动调用被“使用”的对象的Dispose。
连接异常
1、DataException类:表示使用ADO.NET组件发生错误时引发的异常
2、DBConcurrencyException类:在更新操作过程中受影响的行数等于0时,由DataAdapter所引发的异常。
3、SqlException类:当SQL Server返回警告或错误时引发的异常。无法继承此类。
属性
任何时候只要SQL Server.NET数据提供程序遇到服务器生成的错误,就会创建该类。SqlException始终包含至少一个SqlError实例。
1、严重程度等于或者小于10的消息是信息性消息,它们指示由用户输入信息中的错误所导致的问题。严重程度11至16的消息是由用户生成的,可以由用户更正。严重程度17、18或19的错误时,虽然可能无法执行特定语句,但仍可以继续工作。
2、当严重程度等于或者小于19时,SqlConnection保持打开状态。当严重程度等于或大于20时,服务器通常会关闭SqlConnection。但是,用户可以重新打开连接并继续操作。在这两种情况下,执行命令的方法都会生成SqlException。
Private void btnTest_Click(object sender,System.EventArgs e)
{
string strConUnusePool=”Server=localhost;Integrated Security-SSPI;database=mydatabase;pooling=false”;
string strConusePool=”Server=localhost;Integrated Security=SSPI;database=mydatabase;”
+”pooling=true;connction lifetime=5;”;
Int nConNum=50;
//计算不使用连接池创建连接的时间
DateTime dtStart=DateTime.Now;
for(int i=1;i<=nConNum;i++)
{
Using(SqlConnection con=new SqlConnection(strConUnusePool))
{con.Open();con.Close();}
}
DateTime dtEnd=DateTime.Now;
TimeSpan ts=dtEnd-dtStart;
lbUnUse.Text=ts.Milliseconds.ToString();
//计算使用连接池的时间
dtStart=DateTime.Now;
for(int i=1;i<=nConNum;i++)
{
Using(SqlConnection con=new SqlConnection(strConusePool))
{con.Open();con.Close();}
}
dtEnd=DateTime.Now;
ts=dtEnd-dtStart;
lbUse.Text=ts.Milliseconds.ToString();
}
Private void btnScurity_Click(object sender,System.EventArgs e)
{
String strCon=”server=(local);database=mydatabase;uid=sa;pwd=111;pooling=false;”;
SqlConnection con=new SqlConnection(strCon);
String strSql=”select * from ScoreTable where UserName=’”+tbUserName.Text.Replace(“’”,”””);
SqlCommand com=new SqlCommand(strSql,con);
con.Open();
SqlDataReader sdr=com.ExecuteReader();
if(sdr.Read())
MessagerBox.Show(“Authenticated”);
else
MessageBox.Show(“Invalid User”);
sdr.Close();
con.Close();
}
使用命令最佳练习
1、Command对象的使用
使用SqlCommand的最佳练习
使用Prepare方法
显示指定架构和元数据
测试Null
把Null作为参数值传递
执行事务
Command对象的使用
2、方法和描述
Cancel:取消数据命令的执行
CreateParameter:创建一个新的参数
ExecuteNonQuery:执行命令并返回受影响的行数
ExecuteReader;执行命令并返回生成的DataReader
ExecuteScalar:执行查询并返回结果集中的第一行的第一列
ExecuteXmlReader:执行命令并返回生成的XMLReader
Prepare:在数据源上创建一个准备好的命令版本
ResetCommandTimeOut:将CommandTimeOut属性重置为默认值
3、DataReader
当数据命令返回结果集时,用DataReader来检索数据
DataReader对象返回一个来自数据命令的只读的、只能向前的数据流
内存中每次仅有一行数据行,因此开销很少。
4、ExecuteScalar和ExecuteNonQuery
如果想返回像Count(*)、Sum(Price)或Avg(Quantity)的结果那样的单值,可以使用Command.ExecuteScalar。
因为单独一步就能完成,所以ExecuteScalar不仅简化了代码,还提高了性能;要是使用DataReader就需要两步才能完成(即,ExecuteReader+取值)。
使用不返回行的SQL语句时,例如修改数据(例如INSERT、UPDATE或DELETE)或仅返回输出参数或返回值,请使用ExecuteNonQuery。这避免了用于创建空DataReader的任何不必要处理。
5、使用SqlCommand的最佳练习
存储过程是SQLServer数据库的一个重要特色
存储过程执行效率比SQL文本命令要高得多
提高了程序的复杂性
存储过程中可以使用变量和条件
可以在存储过程中使用参数
如果调用存储过程,将SqlCommand的CommandType属性指定为StoredProcedure的CommandType。这样通过将该命令显示标识为存储过程,就不需要在执行之前分析命令。
使用Prepare方法
对于重复作用与数据源的参数化命令,Command.Prepare方法能提高性能。
对于一些数据源(例如SQL Server 2000),命令是隐式优化的,不必调用Prepare。
对于其他(例如SQLServer7.0)数据源,Prepare会比较有效。
1、测试Null:
如果表(在数据库中)中的列允许为空,就不能测试参数值是否“等于”空
Select * from Customers where ((LastName=@LastName) or (LastName is null and @LastName is null))
2、把Null作为参数值传递;
对数据库的命令中,当把空值作为参数值发送时,不能使用null(Visual Basic.NET中的Nothing)。而需要使用DBNull.Value。
事务处理
ADO.NET的事务模型已经更改
1、在AOD中,当调用StartTransaction时,调用之后的任何更新操作都被视为是事物的一部分。但是,在ADO.NET中,当调用Connection.BeginTransaction时,会返回一个Transaction对象,需要把它与Command的Transaction属性联系起来。这种设计可以在一个单一连接上执行多个根事务。
2、如果未将Command.Transaction属性设置为一个针对相关的Connection而启动的Transaction,那么Command就会失败并引发异常。
Class TransDemo
{
Public void DoDemo()
{
SqlConnection con=new SqlConnection(“Server=localhost;Integrated Security=SSPI;database=mydatabase;”);
SqlCommand cmdUpdateScore=new SqlCommand(“UpdateScore”,con);
cmdUpdateScore.CommandType=CommandType.StoredProceduce;
cmdUpdateSocre.Parameters.Add(new SqlParameter(“@username”,”周润发”));
cmdUpdateScore.Parameters.Add(new SqlParameter(“@score”,”700”));
con.Open();
SqlTransaction trans=con.BeginTransaction();
cmdUpdatScore.Transaction=trans;
try
{
cmdUpdateScore.ExecuteNonQuery();
trans.Commit();//no error so commit the transaction
}
catch
{
Trans.Rollback();//rollback the update
}
}
}
议程
1、ADO.NET架构
2、使用ADO.NET连接最佳练习
3、使用ADO.NET命令最佳练习
4、使用DataReader、DataSet、DataAdapter
5、使用ADO.NET的其他技巧
使用DataReader、DataSet、DataAdapter
DataSet是一种代表关系数据的内存驻留结构
DataTableCollection
DataTable
Columns
DataColumn
Rows
DataRow
Constraints
Constraint
DataRelationCollection
DataRelation
ADO.NET
DataProvider
Connection<->DataAdapter对象 <-> DataSet <-> Data Use Application
<->Command对象 Windows Forms
DataReader对象 Web Forms
Others
1、执行以下操作使用DataSet:
在结果的多个离散表之间进行导航。
操作来自多个数据源(例如,来自多个数据库、一个XML文件和一个电子表格的混合数据)的数据
在各层之间交换数据或使用XML Web服务。
重用同样的行组,以便通过缓存获得性能改善(例如排序、搜索或筛选数据)。
每行执行大量处理。
2、对于下列情况,要在应用程序中使用DataReader:
不需要缓存数据。
要处理的结果集太大,内存中放不下。
一旦需要以只进、只读方式快速访问数据。
3、DataReader的常见问题:
在访问相关Command的任何输出参数之前,必须关闭DataReader。完成读数据之后总是要关闭DataReader。
当访问列数据时,使用类型化访问器
一个单一连接每次只能打开一个DataReader。
默认情况下,DataReader每次Read时都要把整行加载到内存。这允许在当前行内随即访问列。如果不需要这种随即访问,为了提高性能,就把CommandBehavior.SequentialAccess传递给ExecuteReader调用
如果已经完成读取来自DataReader的数据,但仍然有大量挂起的未读结果,就在调用DataReader的数据,但仍然有大量挂起的未读结果,就在调用DataReader的数据,但仍然有大量挂起的未读结果,就在调用DataReader的Close之前先调用Command的Cancel。
4、二进制大对象(BLOB)
用DataReader检索二进制大对象(BLOB)时,应该吧CommandBehavior.SequentialAccess传递给ExecuteReader方法调用。
SequentialAccess将DataReader的行为设置为只加载请求的数据。然后还可以使用GetBytes或GetChars控制每次加载多少数据。
Public void ExecuteNonQuery()
{
SqlConnection con=new SqlConnection(“Server=localhost;Integrated Security=SSPI;database-mydatabase;”);
SqlCommand cmdUpdateSales=new SqlCommand(“Update scoretable set score=score+200 where username=’who’”,con);
cmdUpdateSales.CommandType=CommandType.Text;
con.Open();
cmdUpdateSales.ExecuteNonQuery();
con.Close();
}
Public DataSet ExecuteAndFill()
{
SqlConnection con=new SqlConnection(“Server=localhost;Integrated Security=SSPI;database=mydatabase;”);
SqlDataAdapter da=new SqlDataAdapter(“select username,password,score from scoretable”,con);
DataSet ds=new DataSet();
con.Open();
da.Fill(ds);
return ds;
}
其他技巧
1、避免自动增量值冲突
2、检查开放式并发冲突
3、多线程编程
4、仅在需要的时候才用COM Interop访问ADO
小结
1、ADO.NET架构
2、连接练习
3、命令练习
4、使用DataReader、DataSet、DataAdapter