什么是ADO.NET?
ADO.NET就是一组类库,这组类库可以让我们通过程序的方式访问数据库,就像System.IO下的类用类操作文件一样,System.Data.这组类是用来操作数据库(不光是MSSql Server),它提供了统一的编程接口让操作其它数据库(Access、Oracle等)的方式和操作MSSql Server一致。
Ado.net目的:通过程序访问数据库。
Ado.Net组成:
数据提供程序(常用类):
Connection,用来连接数据库;
Command,用来执行SQL语句;
DataReader只读、只进的结果集,一条一条读取数据(StreamReader、XmlReader微软的类库中这些Reader的使用方式都差不多);
DataAdapter,一个封装了上面3个对象的对象;数据集(DataSet),临时数据库,断开式数据操作。
其它常见类:
ConnectionStringBuilder//自动生成连接字符串;
Parameter//带参数的SQL语句;
Transaction//在ADO.NET中使用事务;
与DataSet相关的类:
DataView//视图类,DataTable中的数据以不同的视角查看
DataRowView//DataView中的行。
DataTable //DataSet中的数据表
DataRow//DataTable中的行
DataColumn//DataTable中的列
DataRealation//DataTable与DataTable的关系
Constraint//DataTable中建立的约束
Ado.Net访问数据的方式:
(1)
1.连接数据用Connection
2.执行SQL语句Command
3.执行完毕之后将结果一条一条返回。DataReader
(2)
使用DataAdapter+DataSet,这种方法本质还是通过Connection、Command、DataReader将数据全部取出来然后放到了DataSet中。//看DataAdapter的构造函数。
Connection
如何让应用程序与数据库建立连接?
Connection对象。Connection就像读取数据库数据之前先要创建一条路,在读取Sql Server数据库使用。
连接数据的步骤:
- 创建SqlConnection对象
- 获取连接字符串
- VS视图-服务器资源管理器-数据库连接上点右键-添加连接在新添的数据库上点右键属性里有连接字符串
- 使用SqlConnectionStringBuilder帮助获取连接字符串
- 使用PropertyGrid控件的SelectedObject属性与SqlConnectionStringBuilder配合使用。
- 打开连接.(只能打开一次,可以多次关闭。测试是否打开时可以使用:ConnectionState枚举)
- 关闭连接//相当于设置了路障
- 释放资源//相当于把路拆了,这块地可以盖楼了。
- 调用Connection.Dispose()【继承自Component类的方法】方法时,内部调用了Close()
- connection不能重复打开。
连接池:InnerConnection。用连接池打开连接:Data Source=.\\sqlexpress;Initial Catalog=MySchool;IntegratedSecurity=True;
ADO.Net中通过SqlConnection类创建到SQLServer的连接,SqlConnection代表一个数据库连接,ADO.Net中的连接等资源都实现了IDisposable接口,可以使用using进行资源管理。
sqlconnection在程序中一直保持它open可以吗?
对于数据库来说,连接是非常宝贵的资源,一定要用完了就close、dispose。【Close以后就可以放到“池”中了,其他链接就可以再次使用了。】
Command
操作Sql Server数据库使用SqlCommand对象,SqlCommand表示向服务器提交的一个命令(SQL语句等) , CommandText属性为要执行的SQL语句.
创建SqlCommand对象:
1.通过new关键字创建
2.通过IDbConnection.CreateCommand()方法创建(编写通用代码的时候使用(多态))
常用的三个方法:
ExecuteNonQuery() 执行对数据库的增删改,返回受影响的行数,适合:insert、delete、update(对于其他语句返回-1)
ExecuteScalar() 执行查询,返回首行首列
ExecuteReader() 执行查询,返回DataReader对象
StatementCompleted事件:
每条SQL语句执行完毕之后触发。多条语句同时执行(用分号隔开),如何获取每条语句所影响的行数?//实际返回值为每条语句所影响的行数的和。
ExecuteScalar
SqlCommand的ExecuteScalar方法用于执行查询,并返回查询所返回的结果集中第一行的第一列,因为不能确定返回值的类型,所以返回值是object类型。
得到自动增长字段的主键值,在values关键词前加上output inserted.Id,其中Id为主键字段名。执行结果就试插入的主键值,用ExecuteScalar执行最方便。(output语句使用的是inserted、deleted两个临时表)
cmd.CommandText =“insert into class(cName,cDescription) output inserted.classId values(‘高三一班’,‘描述’)”;int i = Convert.ToInt32(cmd.ExecuteScalar());
原来的写法:
cmd.CommandText =“insert into class(cName,cDescription) values(‘高三一班’,‘描述’);select @@identity”;int i = Convert.ToInt32(cmd.ExecuteScalar());@@IDENTITY 可以返回当前会话中的所有表中生成的最后一个标识值。
执行查询
执行有多行结果集的用ExecuteReader。
SqlDataReader reader = cmd.ExecuteReader();...
while (reader.Read()){Console.WriteLine(reader.GetString(1));}
reader的强类型的GetString()、GetInt32、GetFloat()、GetDouble()等方法只接受整数参数,也就是序号,用GetOrdinal方法根据列名动态得到序号,更简单的方法reader[‘uUserName’]。
使用reader根据列索引读取列数据而不是列名(列名最后也转化为索引);//一般如果没有特殊情况建议使用索引来获取列信息,不要使用列名(效率较 低)。//根据列名来获取数据的话,比较好的写法是:int c1=reader.GetOrdinal("lie1");object obj1=reader[c1]; reader.GetDataTypeName()//当前列的数据类型。
为什么用using?
Close:关闭以后还能打开。Dispose:直接销毁,不能再次使用。using在出了作用域以后调用Dispose,SqlConnection、SqlDataReader等的Dispose内部都会做这样的判断:判断有没有close,如果没有Close就先Close再Dispose。
DataReader的HasRow实现://判断如果close,则直接抛出异常。
public override boolHasRows { get { if (this.IsClosed) { throw ADP.DataReaderClosed("HasRows"); } return this._hasRows; } }
注意:DataReader必须独享一个Connection 。 (除非设置了允许MARS,多活动结果集,在连接字符串中)。
SqlDataReader使用注意事项
返回reader后数据在哪里?数据库服务器缓存
当使用DataReader的时候必须保证Connection为Open状态;
reader只读(不能通过reader修改数据。)、只进;reader每次读取一条就释放一条所以只能向前不能后退;
由于功能有限,所以读取速度很快,适合从数据库中读取大量数据;
HasRow属性返回是否有行。IsDbNull()判断数据是否为null;
数据库中的类型与C#的不太一样,数据库中的float,得用c#的GetDouble()来获取;
如果返回多个结果集则使用NexResult()方法。通过do-while循环测试。强类型转换时,需要使用IsDbNull()判断获得的数据是否为空。
通过执行ExecuteReader()方法获取输出参数的时候需要将reader.Close()以后才能获取。
Ado.Net连接池
由于每次正常连接数据库都会至少执行3个操作(1.登录数据库服务器2.执行操作3.注销用户),所以每次通过Connection向数据库服务器申请一个连接都比较耗时。【ado.net默认启用了连接池】。
如何清空连接池?Connection的静态方法ClearAllPools()、ClearPool()。
什么情况下需要禁用连接池?
一般都不禁用。尤其是asp.net之类的程序,n多个用户频繁访问,但是大多数用户访问时采用的都是同一个连接字符串。但如果某个应用程序有多个客户端,每个客户端访问时采用的都是各自的连接字符串,这时如果采用连接池,虽然每次打开连接的速度会变快,但是由于“池”的问题同时会保存多个打开的连接对象。
注意:一般一个项目中常用的连接也不过几个而已。
Ado.net连接池使用总结:
1.第一次打开连接会创建一个连接对象。
2.当这个连接关闭时(调用Close()方法时)会将当前那个连接对象放入池中。
3.下一个连接对象,如果连接字符串与池中现有连接对象的连接字符串完全一致(大小写敏感),则会使用池中的现有连接,而不会重新创建一个。
4.只有对象调用Close(),的时候才会放入池中,如果一个连接对象一直在使用,则下次再创建一个连接对象发现池中没有,也会再创建一个新连接对象。在池中的连接对象,如果过一段时间没有被访问则自动销毁。
查询参数
SQL语句使用@ParameterrName表示“此处用参数代替”,向SqlCommand的Parameters中添加参数。参数在SQLServer内部不是简单的字符串替换,SQLServer直接用添加的值进行数据比较,因此不会有注入漏洞攻击。(带参数的sql语句内部是调用了存储过程)。
带参数的一个问题(小bug):
SqlParameter p1=new SqlParameter("@age",0);//只有0会出现意外的问题。当是0的时候会调用另一个重载:SqlParameter(string parameterName,SqlDbType dbType);在0前加object,即有重载对象可以应用。
DataSet(ado.net断开式数据访问)
DataSet是什么?
数据的集合、临时数据库、内存数据库。(B/S程序与C/S程序对DataSet的不同处理方式)。
DataSet和SqlDataReader的对比:
SqlDataReader是连接相关的,SqlDataReader中的查询结果并不是放到程序中的,而是放在数据库服务器中,SqlDataReader只是相当于放了一个指针(游标),只能读取当前游标指向的行,一旦连接断开就不能再读取。这样做的好处就是无论查询结果有多少条,对程序占用的内存都几乎没有影响。
SqlDataReader为速度而生,只读、只进,功能有限。ADO.Net中提供了数据集的机制,将查询结果填充到本地内存中,这样连接断开、服务器断开都不影响数据的读取。
DataSet对于多层应用程序之间传递数据。(现在大都用List<T>)
语句:DataSet dataset = new DataSet(); SqlDataAdapter adapter = new SqlDataAdapter(cmd); adapter.Fill(dataset);
SqlDataAdapter是DataSet和数据库之间沟通的桥梁。数据集DataSet包含若干表DataTable,DataTable包含若干行DataRow。foreach (DataRow row in dataset.Tables[0].Rows) row["Name"]。
可以使用不同的SqlDataAdapter来更新DataTable,如果要是用SqlCommandBuilder自动生成的Command对象的话,必须提供selectCommand,但是可以不用Fill。
- 什么是DataSet?
数据集合、临时数据库、内存数据库。但DataSet是通过DataReader填充的。
2.通过DataAdapter填充DataSet,Fill()也可以分页(并不高效,会在服务器端都把数据查询出来,然后用DataReader跳过前面的数据,只取后面要的数据,真正的分页应该是在数据库中就只查询出当前页的数据。)
3.断开式数据访问,操作完成以后调用Update()方法。
DataSource可以绑定什么类型的数据?
IList接口,包括一维数组。IListSource接口,例如,DataTable和DataSet类。IBindingList接口,例如,BindingList(T) 类。IBindingListView接口,例如,BindingSource类。
注意点:
1.通过DataAdapter的Fill方法填充DataSet中的表。
2.创建DataAdapter的时候,只需指定连接字符串和查询语句,会自动生成SelectCommand。
3.通过SqlCommandBuilder自动创建InsertCommand、DeleteCommand、UpdateCommand,然后就可以调用adapter的Update()方法将 DataTable中的数据更新到数据库中。
3.1通过SqlCommandBuilder来创建Command对象的时候,必须保证在创建adapter的时候的select语句中包含对主键的查询
4.也可以自己指定Command,我们对DataTable的操作,只是为Rows集合中没行的RowState状态做了标记,并没有将该行从Rows集合中真的删 除。
多条件搜索时Where 1=1
当拼接多个where条件时,有时候不知道and|or前面的条件是否存在。往往会在后面跟一个where 1=1,但这种方法并不高效。如果使用这种方式,在数据库中会坐全表扫描(对每行数据都进行扫描,比对。),会无法使用索引等优化查询的策略。且建的索引将会失效。
解决方法:List+string.join
Ado.Net中的存储过程
//通过连接对象创建一个事物
SqlTransaction tran=con.BeginTransaction();
Tran.commit();//提交事务
Tran.rollback();//回滚事务
Ado.Net要知道的东西