在连接式访问数据库方式下,数据库就好比一个水池,你要取水池里的水进行操作就必须先建立一条管道,连接就相当于通向数据库的管道。在操作时连接不可以断开,一旦连接断开将造成数据访问的严重错误。
我们知道真正的水池能建立的管道必定有限,对于数据库也是一样的,同一个数据库能在同一时间接受的连接是有限的。缺点是并发性差,比如针对多并发操作的程序,有点是可以快速高效的操作数据库。
重点:
Ø SqlConnection对象
Ø SqlCommand对象的增、删、改、查操作
Ø SqlDataReader对象从数据库读取数据
Ø 资源释放
预习功课:
Ø SqlConnectioni对象的声明与使用
Ø SqlCommand对象常用方法
Ø SqlDataReader对象如何读取数据
Ø 为何要进行数据连接的资源释放
2.1 进水笼头——建立Connection
Connection表示与数据源之间的连接。可根据Connection对象的各种不同属性来指定数据源的类型、位置及其他属性,可用它来与数据库建立连接或断开连接。其他对象如DataAdapter和Command对象通过它与数据库通信。根据.NET Framework 数据提供程序的不同,也有几种不同的Connection,如针对SQL Server的SqlConnection、针对Oracle的OracleConnection、针对MySQL的MySqlConnection、针对OLEDB的OleDbConnection等。(本节代码示例位置:光盘\code\ch05\01)
1.用SqlConnection连接SQL Server
(1)加入命名空间:
1. using System.Data.SqlClient;
(2)连接数据库:
1. string conString = "data source=127.0.0.1;Database=codematic;user id=sa;
password=";
2. SqlConnection myConnection = new SqlConnection(conString);
3. myConnection.Open();
2.用OracleConnection连接Oracle
首先添加OracleClient的引用,如图5-5所示。
(1)加入命名空间:
1. using System.Data.OracleClient;
(2)连接数据库:
1. string conString = "Data Source=codematic;User ID=codeuser;
Password=user123";
2. OracleConnection myConnection = new OracleConnection(conString);
3. myConnection.Open();
3.用MySqlConnection连接MySQL
在.NET中连接MySQL数据库有两种方法:MySQL Connector/ODBC 和 MySQL Connector/NET,ODBC连接器是符合ODBC标准的交互平台,是.NET访问MySQL数据库的最好的选择。
首先,我们下载安装MySql-connector-net-5.1.5.Data.msi这个组件。如果是默认安装,则可以在C:\Program Files\MySQL\MySQL Connector Net 5.1.5\Binaries\.NET 2.0(这里安装的是MySQL Connector/Net 5.1.5,老的1.1版本是:C:\Program Files\MySQL\MySQL Connector Net 1.0.4\bin\.NET 1.1\)中找到MySql.Data.dll,将该文件复制到项目的bin目录下。
然后在项目引用中添加MySql.Data.dll的引用,如图5-6所示。
实现代码如下。
(1)加入命名空间:
1. using MySql.Data.MySqlClient;
(2)连接数据库:
1. string conString = "server=127.0.0.1;database=mysql;user id=root;
password=123";
2. MySqlConnection myConnection = new MySqlConnection(conString);
3. myConnection.Open();
4.用OleDbConnection连接各种数据源
由于数据源不同,相应的连接字符串也会不同。
(1)加入命名空间:
1. using System.Data.OleDb;
(2)连接SQL Server:
1. string conString = " Provider=SQLOLEDB.1;Persist Security Info=False;
2. User ID=sa;Database=Codematic;Data Source=COMPUTER";
3. OleDbConnection myConnection = new OleDbConnection(conString);
4. myConnection.Open();
(3)连接Access(可通过建立.udl文件来获得字符串):
1. string conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source
2. =C:\\Database1.mdb;Persist Security Info=False";
(4)连接Oracle(也可通过OracleConnection连接):
1. string conString = "Provider=MSDAORA.1;User ID=user; Password=123;
2. Data Source=db;Persist Security Info=False";
从以上几个对象实例对比来看,几个.NET数据提供程序组件模型的基本编程模式相同,只是组件对象的前缀有所区别,正是这种统一编程模型,让我们在做不同类型数据库开发时,变得非常简单。
下面我们主要来研究SqlConnection连接类的重要属性和方法:
属性 |
说明 |
ConnectionString |
连接字符串 |
DataBase |
获取连接打开的数据库名称 |
State |
获取连接对象的状态 |
方法 |
说明 |
Open |
打开数据库连接 |
Close |
关闭数据库连接 |
要建立一个数据库的连接,必须给数据库对象足够的关于它要连接的数据库的信息,这些信息包括数据库服务器名称、数据库名称、数据库账户、账户密码等。由这些链接信息组成的连接字符串叫“数据库连接字符串”。格式如下:
string conn="server=.;database=NIIT;uid=sa;pwd=123456";
下面来解释下数据库连接字符串:
n server:后面跟数据库服务器的名称;可以为本机地址(.、local、或127.0.0.1);或对方数据库服务器名称或IP,如:"ha001.mobile"、"224.68.102.86"。
n database:你要访问的服务器的名称,也可写成"Initial Catalog";
n uid:登录数据库账户的名称,也可写成"user id";
n pwd:登录数据库账户的密码,密码为空时,pwd不能省略,可以这样写"uid=sa;pwd=";
有了数据库连接字符串,就可以直接建立数据库连接对象了:
SqlConnection cn=newSqlConnection(server=.;database=NIIT;uid=sa;pwd=123456");
建立了数据库连接后,如果要通过该连接操作数据库里面的数据,还需要打开该连接:
cn.Open();
如果该数据库连接使用完了,可以调用Close()方法关闭该连接,如:
cn.Close();
要注意每个数据库能接受的连接是有限的,每个连接都可以为一个应用程序服务,所以,数据连接式非常宝贵的资源。因此,在操作数据库连接对象时要注意谨慎,要使用异常结构确保连接使用完后正常释放。
为什么使用异常处理,可能会有如下情况:
n 与数据库的连接中断
n 无法打开数据库
n 无法操作数据
n …
一个完整并且有效的使用数据库连接对象的例子如下:
using System;
using System.Data.SqlClient;
class DataDemo
{
static void Main(string[]args)
{
//通过连接字符串建立数据库连接
SqlConnection cn=newSqlConnection("server=.;database=School;uid=sa;pwd=123456;");
try
{
//打开数据库连接
cn.Open();
Console.WriteLine("连接建立成功!");
}catch(SqlException ex)
{
//要对连接异常做仔细的处理
Console.WriteLine(“连接数据库出错:"+ex.Message);
}
finally
{
//一定要确保数据连接正常释放
cn.Close();
}
}
}
运行结果如果显示"连接建立成功",那么表示你建立的数据库连接是有效的。数据连接一旦建立,我们就可以通过连接对象的State属性获得此连接的状态,连接状态用一个ConnectionState枚举表示,常用的连接状态只有两种:
l Open:表示连接处于打开状态
l Close:表示连接处于关闭状态
我们可以通过判断连接状态来合理的打开或关闭数据库连接,如:
if(cn.State==ConnectionState.Open)
cn.Close();
if(cn.State==ConnectionState.Closed)
cn.Open();
要注意ConnectionState枚举属于System.Data命名空间,在使用这个枚举时别忘了导入这个命名空间。
Sql Server数据库有两种登录方式,一种是SqlServer账户登录方式,一种是Windows集成登录方式。前面我们已经使用过Sql Server账户登录方式了,还可以通过Windows集成验证方式登录,这种登录会把Windows集成验证的安全证书传给数据库服务器,所以不需要密码,要使用Windows登录验证只需修改数据库连接字符串为如下即可:
SqlConnection cn=new SqlConnection("server=.;database=School;uid=sa;pwd=123456;IntergratedSecurity=SSPI");
如前所述,数据库一旦建立,也就是开通了一条存取数据库连接的通道。以数据库连接为基础再结合数据库的操作对象(如下面将介绍的命令对象)就可以存取数据库里的数据了。
2.2 对数据库进行增、删、改、查操作
抽水机:Command
通过Ado.Net中的Command命令对象可以向数据库服务器发送一个操作命令。这些操作命令包括执行某个SQL语句或调用某个存储过程。有了命令对象我们就可以对数据库进行增、删、改、查等操作了。Command命令对象时建立在数据库连接基础上的,所以在调用命令对象操作数据库时要保证数据库连接已经打开。
Ado.Net为我们提供SqlCommand类也在System.Data.SqlClient命名空间下:
属性 |
说明 |
Connection |
Command对象使用的数据库连接 |
CommandText |
执行的SQL语句 |
CommandType |
命令对象的类型,主要包括两种类型,一表示要执行Sql语句, 一种表示要调用存储过程 |
Parameters |
与命令对象关联的参数集合对象 |
Transaction |
与命令对象关联的事物对象 |
方法 |
说明 |
ExecuteNonQuery |
执行不返回行的语句,如UPDATE等 |
ExecuteReader |
返回DataReader对象 |
ExecuteScalar |
返回单个值,如执行COUNT(*) |
2.2.1使用Command步骤:
1、创建数据库连接
2、定义 SQL 语句
3、创建 Command 对象(执行命令前,必须打开数据库连接!)
4、执行命令
SqlCommand类为我们提供了两个常用的方法可以执行查询操作,一个方法是ExecuteScalar()方法,它返回一个值;另外一个是ExecuteReader()方法,它返回一个SqlDataReader()类型的结果集。
要使用ExecuteScalar()方法在构造Sql语句时一般要带聚合函数如:
Select Count(*) From Admin //计算有多少人
Select Average(age) From UserInfo //计算平均年龄
ExecuteScalar()方法一般用在某些特殊的情况下,比如用户验证(判断这个用户是否在该数据库中),再比如求平均值。其返回类型为Object类型的,所以,需要对结果进行强制转换才能得到你想到的值。
部分代码:
SqlConnection connection = newSqlConnection(connString);
string sql = "SELECT COUNT(*) FROMStudent";
connection.Open();// 打开数据库连接
SqlCommand command = newSqlCommand(sql, connection);
int num = (int)command.ExecuteScalar();
综合示例
完成系统登录功能
1、验证管理员的用户名和密码是否存在
2、验证通过,显示管理员主窗体
分析:
ü 处理登录按钮的 Click 事件
ü 定义一个 ValidateUser() 方法
ü 需要 Connection 和 Command 对象
ü 分两步实现验证:
1、建立数据库连接
2、验证用户是否存在
ValidateUser() 方法框架:
ValidateUser() 方法具体实现
第一步:建立数据库连接
-增加 DBHelper 类
第二步:查询用户是否存在
-SELECT COUNT(*)
-ExecuteScalar() 方法查询
// 查询 Student 表使用的 SQL 语句
string sql = string.Format(
"SELECT COUNT(*) FROM Student WHERE LogInId='{0}'
AND LogInPwd='{1}'",txtLogInId, txtLogInPwd);
// 使用的 Command 对象
SqlCommand command = newSqlCommand(sql, DBHelper.connection);
DBHelper.connection.Open();
// 执行查询,返回找到的个数
count = (int)command.ExecuteScalar();
小结:
l Connection 对象的什么方法用来打开和关闭数据库连接?
l ExecuteScalar()方法返回什么?
2.2.2如何对数据库的数据进行增删改?
对于四大操作来说,增、删、改三大操作都是单向的,之所以叫单向操作就是因为这些操作只是修改数据库而不返回数据。只有查是双向操作,因为既需要数据库提交查询命令又需要操作从数据库返回的结果集。所有单向操作使用SqlCommand类的ExecuteNonQuery()方法来执行。
以教务信息系统增加学员为例:
处理“保存”按钮的 Click事件,使用ExecuteNonQuery() 方法向数据库增加记录
部分参考代码:
stringconnStr = "…";
SqlConnection connection = newSqlConnection(connStr);
// 插入记录用的 SQL 语句
string sql = string.Format(
"INSERTINTO Grade (GradeName) VALUES ('{0}')",
txtGradeName.Text);
// 创建 Command 对象
try
{
SqlCommand command= new SqlCommand(sql, connection);
// 打开数据库连接
connection.Open();
// 执行插入命令
int result =command.ExecuteNonQuery();
}catch(SqlException sqle)
{
Console.WriteLine("连接数据库出错"+sqle.Message);
}
finally
{
cn.Close();// 关闭数据库连接
}
//如果是删除数据,则将sql语句改写为Delete语句即可,例如从人员表中删除某条记录
string sql="Delete from Personwhere Id=’ha002.mobile’";
//如果是更新数据,则将sql语句改写为Update语句即可
string strSql2 = "update P_Product set Name='数码4' where Id=53";
2.3 如何从数据库读取数据:输水管--DataReader
通过调用SqlCommand对象的ExecuteReader()可以得到一个SqlDataReader结果集对象。SqlDataReader是一个只进式的记录读取器,它可以一条一条读取检索到的数据。如果数据库是水池,连接对象是管道,那么SqlDataReader就是水龙头,打开水龙头就会不断的流出。
因为SqlDataReader每次只能读取一条记录,所以你需要借助一个循环来读取所有的记录。你可以通过SqlDataReader的Read方法来读取下一条记录。同时Read方法还会返回一个bool型的值,借助这个值就可以判断是否已经读完,如果Read返回true,表示没有读完,否则表示读完。
通过SqlDataReader获取当前记录后,有四种方式获取该记录的列值。
2.3.1案例:使用 DataReader 查询数据
分析:
1. 需要自动添加“年级”组合框中的年级名称;
2. 处理窗体 Load 事件
3. 使用 DataReader 读取年级名称
关键代码:
// 执行查询省略…
SqlDataReader dataReader =command.ExecuteReader();
string gradeName =""; // 年级名称
// 循环读出所有的年级名,并添加到年级列表框中
while (dataReader.Read())
{ /*循环读取数据行添加到组合框中*/
gradeName = (string)dataReader[0];
cboGrade.Items.Add(gradeName);
}
dataReader.Close();
2.3.2 DataReader 的主要成员
属性 |
说明 |
HasRows |
是否返回了结果 |
方法 |
说明 |
Read |
前进到下一行记录 |
Close |
关闭 DataReader 对象 |
DataReader 使用步骤小结
1、创建 Command 对象
2、调用 ExecuteReader() 创建DataReader 对象
3、使用 DataReader 的Read() 方法逐行读取数据
4、读取某列的数据,(type)dataReader[ i]
5、关闭 DataReader 对象
//注意:DataReader使用后必须关闭
//第4步获取某列值的方法有如下四种方式:
ü 第一种:通过列号
(type)dataReader[i]; //i:表示该列的列号,取值为0…n
ü 第二种:通过列名
(type)dataReader["列名"]
ü 第三种:通过GetValue()和列号
(type)dataReader.GetValue(i); //i: 表示该列的列号,取值为0…n
ü 通过GetX方法和列号
dataReader.GetString(i),dataReader.GetInt32(i)等;
这四种读取方式的主要区别在于效率的问题,前三种返回的都是object类型的数据,你需要进行拆箱操作(强制类型转换)才能使用该列的值。只有第四种返回的是具体数据类型的值,效率会高些。第四种方式应该根据数据库列的具体类型调用不同的Get方法,比如数据库类型是DateTime,则需要调用GetDateTime(i)方法。另外,第二种方法也比较常用。
2.3.3得到DataReader的列信息
dataReader.FieldCount 获取当前行中的列数
dataReader.GetFieldType(序号) 获取是对象的数据类型的 Type
dataReader.GetDataTypeName(序号) 获取源数据类型的名称
dataReader.GetName(序号) 获取指定列的名称
dataReader.GetOrdinal(序号) 在给定列名称的情况下获取列序号
2.3.4得到数据表的信息
DataTable dt=dr.GetSchemaTable();
2.3.5使用DataReader获得最佳性能的技巧
在使用带参数的Command前,必须关闭DataReader。
完成读数据之后一定要关闭DataReader。如果使用Connection只返回DataReader,那么关闭DataReader之后立刻关闭它。另外一个显式关闭Connection的方法是将CommandBehavior.CloseConnection传递给ExecuteReader方法,以确保关闭DataReader时相应的连接也被关闭。如果从一个方法返回DataReader,而且不能控制DataReader的相关连接的关闭,则这样做特别有用。
不能在层之间远程访问DataReader。DataReader是为已连接好的数据访问而设计的。
当访问列数据时,使用类型化访问器,例如GetString、GetInt32等。这使你不用将GetValue返回的Object强制转换成特定类型。
一个单一连接每次只能打开一个DataReader。如果想在相同的数据存储区上同时打开两个DataReader,则必须显式创建两个连接,每个DataReader一个。这是ADO.NET为池化连接的使用提供更多控制的一种方法。
在默认情况下,DataReader每次Read时都要将整行加载到内存。这允许在当前行内随机访问列。如果不需要这种随机访问,为了提高性能,则将CommandBehavior.SequentialAccess传递给ExecuteReader调用。这将DataReader的默认行为更改为仅在请求时将数据加载到内存。注意,CommandBehavior.SequentialAccess要求顺序访问返回的列。也就是说,一旦读过返回的列,就不能再读它的值了。
如果已经读取了来自DataReader的数据,但仍然有大量挂起的未读结果,则在关闭DataReader之前先要取消Command。因为取消Command可使服务器放弃这些结果,从而释放服务器的资源。
2.4 随用随关,释放资源
对于C#程序员来说,确保始终关闭Connection和DataReader对象的一个方便的方法就是使用using语句。using语句在离开自己的作用范围时,会自动调用被"使用"的对象的Dispose。例如:
1. string connectionString = "data source=127.0.0.1;Database=codematic;
2. user id=sa;password=";
3. using (SqlConnection myConnection = new SqlConnection(connectionString))
4. {
5. SqlCommand cmd = myConnection.CreateCommand();
6. cmd.CommandText = "SELECT * FROM P_Product";
7. myConnection.Open();
8. using (SqlDataReader dr = cmd.ExecuteReader())
9. {
10. while (dr.Read())
11. {
12. Response.Write(dr.GetInt32(0).ToString()+","+dr.GetString(1)
+"<br>");
13. }
14. }
}