4.3 操作数据库
学习目标
熟悉ADO.NET的基本对象模型。掌握Connection、Command、Parameter、DataReader的详细使用方法,熟悉连接池的使用方法。
本节示例代码参考:/Code/ch04/StudyCommand,/Code/ch04/StudyDataReader。
4.3.1 ADO.NET对象模型
以前,数据处理主要依赖于基于连接的双层模型。当数据处理越来越多地使用多层结构时,程序员正在向断开方式转换,以便为他们的应用程序提供更佳的可缩放性。而ADO.NET使用.NET Framework数据提供程序和DataSet技术的结合恰好解决了这一问题。
其中.NET Framework数据提供程序是专门为数据处理以及快速地只进、只读访问数据而设计的组件。Connection对象提供与数据源的连接。Command对象能够访问用于返回数据、修改数据、运行存储过程以及发送或检索参数信息的数据库命令。DataReader从数据源中提供高性能的数据流。最后,DataAdapter提供连接 DataSet对象和数据源的桥梁。DataAdapter使用Command对象在数据源中执行SQL命令,以便将数据加载到DataSet中,并使对DataSet中数据的更改与数据源保持一致。而ADO.NET DataSet专门为独立于任何数据源的数据访问而设计。因此,它可以用于多种不同的数据源,用于XML数据,或用于管理应用程序本地的数据。DataSet包含一个或多个DataTable对象的集合,这些对象由数据行和数据列以及有关DataTable对象中数据的主键、外键、约束和关系信息组成。
图4-5说明.NET Frame work数据提供程序与DataSet之间的关系。
1.选择DataReader或DataSet
在决定应用程序应使用DataReader还是应使用DataSet时,应考虑应用程序所需的功能类型。DataSet用于执行以下功能。
(1)在应用程序中将数据缓存在本地,以便可以对数据进行处理。如果只需要读取查询结果,DataReader是更好的选择。
(2)在层间或从XML Web服务对数据进行远程处理。
(3)与数据进行动态交互,例如绑定到Windows窗体控件或组合并关联来自多个源的数据。
(4)对数据执行大量的处理,而不需要与数据源保持打开的连接,从而将该连接释放给其他客户端使用。
如果不需要DataSet所提供的功能,则可以使用DataReader以只进、只读方式返回数据,从而提高应用程序的性能。虽然DataAdapter使用DataReader来填充DataSet的内容,但可以使用DataReader来提高性能,因为这样可以节省DataSet所使用的内存,并将省去创建DataSet并填充其内容所需的处理。
2.XML和ADO.NET
ADO.NET利用XML的力量来提供对数据的断开式访问。ADO.NET的设计与.NET Framework中XML类的设计是并进的,它们都是同一个结构的组件。
ADO.NET和.NET Framework中的XML类集中于DataSet对象。无论XML源是文件还是XML流,都可以用来填充DataSet。无论DataSet中数据的数据源是什么,DataSet都可以作为符合万维网联合会(W3C)标准的XML进行编写,并且将其架构包含为XML架构定义语言(XSD)架构。由于DataSet固有的序列化格式为XML,因此是在层间移动数据出色的媒介,这使DataSet成为在远程向XML Web服务发送数据和架构上下文以及从XML Web服务接收数据和架构上下文的最佳选择。
4.3.2 Connection
ADO.NET中的Connection对象属于连接对象,是ADO.NET技术中最重要的对象,因为几乎所有和数据库关联的操作都要建立连接成功后方能进行,即使要使用断开数据库操作也要通过连接把数据信息填充到DataSet数据集中方能进行,可见连接对象的重要性。当然该对象的功能就是创建与指定数据库的连接并完成初始化工作。下面给出在程序中声明Connection对象实例的代码(基于C#)。
使用System.Data.OleDb命名空间,连接Access数据库:
OleDbConnection conn = new OleDbConnection("Provider=microsoft.jet. oledb.4.0; Data Source="+Server.MapPath("path\\DataBaseName.mdb"));
使用System.Data.OleDb命名空间,连接SQL Server数据库:
OleDbConnection conn = new OleDbConnection("Provider=SqlOleDb; DataBase=DataBaseName; uid=sa; pwd=; Server=ServerName"); OleDbConnection conn = new OleDbConnection("Provider=SQLOLEDB; Persist Security Info=false; User ID=sa; Initial Catalog=testDB; Password=1234; Data Source=DUANKEQI");
连接字符串中Persist Security Info关键字的默认设置为false。如果将该关键字设置为true或yes,将允许在打开连接后,从连接中获得涉及安全性的信息(包括用户标识和密码)。如果在建立连接时必须提供用户标识和密码,最安全的方法是在使用信息打开连接后丢弃这些信息,在Persist Security Info设置为false或no时会发生这种情况。当向不可信的源提供打开的连接,或将连接信息永久保存到磁盘时,这点尤其重要。如果将Persist Security Info保持为false,可帮助确保不可信的源无法访问连接中涉及安全性的信息,并帮助确保任何涉及安全性的信息都不会随连接字符串信息在磁盘上持久化。
实际上上述不同连接数据库的方法关键在于连接字符串的写法不同,实质是一样的,就是创建了一个Connection连接对象的一个实例,如果连接操作SQL Server数据库建议选用System.Data.SqlClient类,因为System.Data.SQLClient是MicroSoft公司专门为SQL Server数据库开发设计的连接方法,效率比OleDb方法要高。
本章以后的例子都选择使用System.Data.SqlClient类操作SQL Server 2005数据库。
使用System.Data.SqlClient命名空间,连接SQL Server数据库,必须在程序页面的上方导入“System.Data.SqlClient”命名空间。下面给出简单格式:
SqlConnection conn=new SqlConnection("Server=ServerName; uid=sa; pwd=; DataBase = DataBaseName ");
连接对象的实例化存在多种形式,其实面向对象编程的每个类在实例化时基本上都存在多种形式,其实无非就是如何给其中的属性赋值的问题,可以在实例化时直接给指定属性赋值,也可以在实例化后再给指定属性赋值。接下来介绍的Connection对象实例化的方法在其他类实例化时通用。
(1)例如,在操作SQL数据库时,要实例化一个Connection对象,首先应导入“System.Data.SqlClient”命名空间,然后在Page_Load事件中编写如图4-6所示的代码,在写完第19行代码的最后一个小括号后,会看到提示信息,实际上每个类在实例化时都会在这一步给出提示,这里的提示就是给出了类对象的实例化方法,图4-6中显示“总数2”就表明有两种方法可以实例化该对象,而在“总数2”的前面的“1”表明当前是第一种实例化的方法,并且后面“SqlConnection. SqlConnection()”的括号内没有任何参数就是说,这种方法实例化不需要给任何参数赋值,直接输入结束小括号即可。
(2)在图4-6的状态中可以使用上下箭头改变使用的方法,如按键盘上的向下的箭头,显示效果如图4-7所示。很明显第二种方法存在参数connectionString,这实际上就是连接数据库的字符串,具体也就是连接对象的ConnectionString属性的取值问题,也就是说可以直接在实例化连接对象时顺便给该属性赋值,也可以在实例化后再单独给该属性赋值,图4-6给出的第一种方法就是先实例化对象,再单独给ConnectionString属性赋值,而图4-7给出的第二种方法就是在实例化连接对象的同时顺便给ConnectionString属性赋值。
(3)下面给出两种方法的完整代码。
方法1:
SqlConnection conn = new SqlConnection(@"server=ServerName;uid=sa ;pwd=sa;database=dbName");
方法2:
SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server= ServerName;uid=sa;pwd=sa;data base=dbName ";
实际上,不管是使用什么方法创建的连接对象,它都具备表4-5给出的Connection连接对象的常用属性和方法。
4.3.3 Command
数据库连接成功后就可以使用ADO.NET中的其他对象模型来完成对数据库的具体操作。而Command对象是操作数据库使用最为频繁的对象。
Command对象的功能是在创建成功的数据库连接上执行SQL命令(包括普通的SQL语句、存储过程)。可以使用Command关联的方法和属性来指定Command对象的工作方式,例如执行正常的SQL语句还是打开一个完整的数据表或者是执行存储过程。表4-6给出了Command对象的常用属性和方法。
当CommandType属性设置为StoredProcedure时,CommandText属性应设置为要访问的存储过程的名称。CommandType属性设置为TableDirect时,应将CommandText属性设置为要访问的表的名称。当CommandType属性设置为Text时,CommandText属性应设置为要访问的普通SQL语句,也是默认值。
只有用于OLE DB的.NET Framework数据提供程序才支持TableDirect。当CommandType设置为TableDirect时,不支持对多个表的访问。
接下来通过实例讲解Command对象的各种属性方法的应用。首先给出数据库的完整脚本代码。
create database studentDB go use studentDB go create table student( id int identity(1,1) primary key, name varchar(20) not null, sex char(2) not null, city varchar(20) not null ) go insert into student(name,sex,city) values('张三','男','北京') insert into student(name,sex,city) values('王晓慧','女','上海') insert into student(name,sex,city) values('李志强','男','北京') go select * from student go
1.使用Command对象读取数据库信息
(1)新建ASP.NET网站项目StudyCommand,删除默认Default.aspx页面,添加页面StudyCommand001.aspx。在页面StudyCommand001.aspx的设计窗口中添加GridView控件作为显示数据信息的容器,然后直接进入StudyCommand001.aspx.cs代码窗口,必须要在代码窗口的上面先导入SqlClient命名空间如“using System.Data. SqlClient;”,然后编写Page_Load初始化代码,接下来给出完整Page_Load初始化代码,其中编写了大量注释,方便读者阅读。
protected void Page_Load(object sender, EventArgs e) { //实例化一个连接对象conn SqlConnection conn = new SqlConnection(); //定义连接对象的数据库连接字符串 conn.ConnectionString = @"server=PC-200807311524;uid=sa ;pwd=sa;database=studentDB"; //打开连接对象 conn.Open(); //实例化一个命令对象cmd SqlCommand cmd = new SqlCommand(); //定义命令对象的具体命令 cmd.CommandText = "select * from student order by id"; //定义命令对象的具体命令的类型 //CommandType.Text表示为普通SQL语句 //CommandType.TableDirect表示为表名 //CommandType.StoredProcedure表示为存储过程 //默认没有指定,则命令对象的CommandType属性为CommandType.Text cmd.CommandType = CommandType.Text; //指定命令对象所使用的连接对象为conn cmd.Connection = conn; //调用命令对象的cmd.ExecuteReader()方法生成一个SqlDataReader数据集 SqlDataReader sdr = cmd.ExecuteReader(); //将生成的数据集对象sdr作为GridView1数据控件的数据源 GridView1.DataSource = sdr; //为GridView1数据控件绑定数据 GridView1.DataBind(); //关闭SqlDataReader数据集对象sdr sdr.Close(); //释放SqlDataReader数据集对象sdr sdr.Dispose(); //关闭SqlConnection数据库连接对象conn conn.Close(); //释放SqlConnection数据库连接对象conn conn.Dispose(); }
(2)按Ctrl+F5组合键,运行显示效果如图4-8所示。
2.使用Command对象向数据库添加信息
这里主要使用Command对象的Execute NonQuery方法。通常,可以使用命令对象的Execute NonQuery执行编录操作(例如查询数据库的结构或创建诸如表等的数据库对象),或通过执行UPDATE、INSERT或DELETE语句更改数据库中的数据。
虽然ExecuteNonQuery不返回任何行,但是映射到参数的任何输出参数或返回值都会用数据进行填充。对于UPDATE、INSERT和DELETE语句,返回值为该命令所影响的行数。如果发生异常或其他未知错误,返回值为一1。
(1)在ASP.NET网站项目StudyCommand中添加页面StudyCommand002. aspx。在页面StudyCommand 002.aspx的设计窗口中添加相关控件布局添加信息的表单如图4-9所示。
(2)然后在StudyCommand002.aspx页面的代码窗口StudyCommand002.aspx.cs 程序页面的Page_Load事件中编写性别和城市两个项目控件的信息初始化代码,由 于初始化只是用户第一次访问该页面要执行的代码,后续的反复添加信息总会保留 页面的状态信息,所以对于性别和城市两个项目的初始化代码必须放在用户第一次 访问的位置编写,即:
if(!Page.IsPostBack) { //用户第一次访问的代码编写位置; }
在StudyCommand002.aspx.cs程序页面的Page_Load事件中编写的性别和城市两个项目控件的信息初始化代码如下。
protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { //定义性别信息的数组共2个元素 string[] sexArray = new string[2]; sexArray[0] = "男"; sexArray[1] = "女"; //定义城市信息的数组共5个元素 string[] cityArray = new string[5]; cityArray[0] = "北京"; cityArray[1] = "上海"; cityArray[2] = "天津"; cityArray[3] = "广州"; cityArray[4] = "深圳"; //给性别的控件rBtnSex指定数据源 rBtnSex.DataSource = sexArray; //给性别的控件rBtnSex绑定数据 rBtnSex.DataBind(); //指定性别的控件rBtnSex的项目布局方式为水平布局显示//Repeat Direction.Horizontal,默认为垂直显示RepeatDirection.Vertical rBtnSex.RepeatDirection = RepeatDirection.Horizontal; //指定性别的控件rBtnSex的项目索引号为0的项目为选择项 rBtnSex.Items[0].Selected = true; //给城市的控件ddlCity指定数据源 ddlCity.DataSource = cityArray; //给城市的控件ddlCity绑定数据 ddlCity.DataBind(); //指定城市的控件ddlCity的项目索引号为0的项目为选择项 ddlCity.Items[0].Selected = true; } }
(3)在图4-9中双击“确定”按钮进入该按钮的代码编写位置,编写添加信息的具体代码,当然由于涉及SQL数据库操作,必须在StudyCommand002.aspx.cs代码窗口上方首先导入SqlClient命名空间如"using System.Data.SqlClient;"。接下来给出“确定”按钮的单击事件代码。
protected void btnConfirm_Click(object sender, EventArgs e) { //获取用户在表单中添加的信息 string Name = txtName.Text.Trim(); string Sex = rBtnSex.SelectedValue; string City = ddlCity.SelectedValue; //构造添加的命令SQL语句 string sqlString = "insert into student(name,sex,city) values ('" + Name + "','" + Sex + "','" + City + "')"; //实例化一个连接对象conn SqlConnection conn = new SqlConnection(); //定义连接对象的数据库连接字符串 conn.ConnectionString = @"server=PC-200807311524;uid=sa; pwd=sa;database=studentDB"; //打开连接对象 conn.Open(); //实例化一个命令对象cmd,同时指定其中的具体命令和使用的连接对象 SqlCommand cmd = new SqlCommand(sqlString, conn); //调用命令对象的ExecuteNonQuery()方法执行SQL指令 //一般对于insert update delete都用ExecuteNonQuery()方法 执行SQL指令 //ExecuteNonQuery()方法返回的是所影响的行数 //如果发生异常或其他未知错误ExecuteNonQuery()方法则返回-1 if (cmd.ExecuteNonQuery() == -1) { //添加失败 Response.Write("添加失败"); } else { //添加成功 Response.Write("添加成功"); } conn.Close(); conn.Dispose(); }
(4)然后按Ctrl+F5组合键,运行显示效果如图4-10所示。
(5)在图4-10中直接单击“确定”按钮则显示如图4-11所示,并没有完成添加是由于验证控件起了作用,用Required FieldValidator限制用户必须要填写姓名,错误提示“*”也可以修改成其他错误提示信息,如“姓名必须填写”。
(6)在图4-11中正确填写如“张婷婷 女 北京”,然后单击“确定”按钮,显示如图4-12所示。
(7)此时如果再选择运行StudyCommand001.aspx,运行显示效果如图4-13所示。
3.使用Command对象更新、删除数据库信息
使用Command对象完成对数据库信息的更新和删除操作其实主要也是使用命令对象的ExecuteNonQuery方法来完成的。主要问题是如何构造更新、删除的表单环境以及SQL语句。接下来举例说明。
(1)删除功能。
①在ASP.NET网站项目StudyCommand中添加页面StudyCommand003.aspx。在页面StudyCommand003.aspx的设计窗口添加相关控件,布局添加信息的表单如图4-14所示,其中给“删除”按钮的onclientclick属性中嵌入了一段JavaScript脚本,目的是为了给出删除确认的提示对话框。
②然后进入StudyCommand003.aspx.cs代码窗口编写Page_Load事件初始化代码,由于信息删除后需要使得编号重新绑定,故把编号绑定的处理作为一个独立的方法BindToddlID()
,然后在Page_Load事件初始化代码中和删除处理后都可以直接调用。接下来给出方法BindToddlID()
的完整代码。
private void BindToddlID() { SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=PC-200807311524;uid=s a;pwd=sa;database=studentDB"; conn.Open(); string sqlString = "select id from student order by id asc"; SqlCommand cmd = new SqlCommand(sqlString, conn); SqlDataReader sdr = cmd.ExecuteReader(); ddlID.DataSource = sdr; ddlID.DataTextField = "id"; ddlID.DataValueField = "id"; ddlID.DataBind(); sdr.Close(); sdr.Dispose(); conn.Close(); conn.Dispose(); }
编写完BindToddlID()方法的完整代码后,那么Page_Load事件初始化代码就可以简化如下。
protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { BindToddlID(); } }
③在图4-14中双击“删除”按钮进入该按钮的单击事件代码编写位置,编写删除的完整代码,接下来给出删除的完整代码。
protected void btnDelete_Click(object sender, EventArgs e) { SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=PC-200807311524;uid=s a;pwd=sa;database=studentDB"; conn.Open(); string id = ddlID.SelectedValue; string sqlString = "delete from student where id=" + id; SqlCommand cmd = new SqlCommand(sqlString, conn); if (cmd.ExecuteNonQuery() == -1) { //删除失败 Response.Write("删除失败"); } else { //删除成功 Response.Write("删除成功"); } conn.Close(); conn.Dispose(); BindToddlID();//重新绑定数据给选择的编号ddlID }
④然后按Ctrl+F5组合键,在运行显示的窗口中“选择编号”为“3”,然后直接单击“删除”按钮显示效果如图4-15所示。
⑤图4-15中显示的对话框就是给“删除”按钮添加了一个onclientclick属性所嵌入的JavaScript脚本的效果。单击“取消”按钮则不删除,单击“确定”显示效果如图4-16所示。
从图4-16中也可以看出已经不存在编号为3的信息,就是因为刚才的动作已经把数据库表student中的编号为3的记录删除了。至此删除功能完成,接下来完成更新功能。
(2)更新功能。
①在图4-14中双击“更新”按钮进入该按钮的代码编写位置,编写实现更新页面跳转的代码,完整代码如下。
protected void btnUpdate_Click(object sender, EventArgs e) { string id = ddlID.SelectedValue; Response.Redirect("Update.aspx?id=" + id); }
②其实目的就是为了给实际的更新表单页面传递一个要更新的信息id。然后在ASP.NET网站项目StudyCommand中添加页面Update.aspx。在页面Update.aspx的设计窗口添加相关控件布局更新信息的表单如图4-17所示,其中的隐藏控件hfID目的就是为了获取要更新的记录ID。
③然后进入Update.aspx.cs代码窗口完成Page_Load事件的初始化代码,目的就是为了在表单中显示出用户要修改的记录的具体信息。完整Page_Load事件代码如下,代码中存在详细注释,不再解释。
protected void Page_Load(object sender, EventArgs e) { //处理接收的id值是否存在,不存在就重定向到StudyCommand003.aspx选择 string ID = string.Empty; if (Request.QueryString["id"] == null) { Response.Redirect("StudyCommand003.aspx"); } else { ID = Request.QueryString["id"].ToString(); } //获取id的姓名Name、性别Sex、城市City string Name = string.Empty; string Sex = string.Empty; string City = string.Empty; SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=PC-200807311524;uid=sa; pwd=sa;database=studentDB"; conn.Open(); SqlCommand cmd = new SqlCommand("select name,sex,city from student where id=" + ID, conn); SqlDataReader sdr = cmd.ExecuteReader(); if (sdr.Read())//如果从sdr中获取到了数据 { Name = sdr["name"].ToString(); Sex = sdr["sex"].ToString(); City = sdr["city"].ToString(); } else//没有从sdr中获取到数据 { Response.Write("ID无效"); Response.End(); }//end if if (!Page.IsPostBack) { //给姓名文本框指定Name的值 txtName.Text = Name; //给隐藏控件hfID指定ID值,便于构造条件更新语句 hfID.Value = ID; //定义性别信息的数组共2个元素 string[] sexArray = new string[2]; sexArray[0] = "男"; sexArray[1] = "女"; //定义城市信息的数组共5个元素 string[] cityArray = new string[5]; cityArray[0] = "北京"; cityArray[1] = "上海"; cityArray[2] = "天津"; cityArray[3] = <sup>"</sup>广州"; cityArray[4] = "深圳"; //给性别的控件rBtnSex指定数据源 rBtnSex.DataSource = sexArray; //给性别的控件rBtnSex绑定数据 rBtnSex.DataBind(); //指定性别的控件rBtnSex的项目布局方式 //为水平布局显示RepeatDirection.Horizontal //默认为垂直显示RepeatDirection.Vertical rBtnSex.RepeatDirection = RepeatDirection.Horizontal; //遍历性别的控件rBtnSex的项目使得性别为Sex值的项目为当前选择项目 foreach (ListItem item in rBtnSex.Items) { if (item.Value == Sex) { item.Selected = true; } } //给城市的控件ddlCity指定数据源 ddlCity.DataSource = cityArray; //给城市的控件ddlCity绑定数据 ddlCity.DataBind(); //遍历城市的控件ddlCity的项目使得城市为City值的项目为当前选择项目 for (int index = 0; index < ddlCity.Items.Count; index++) { if (ddlCity.Items[index].Value == City) { ddlCity.Items[index].Selected = true; } } }//end if }
④在图4-17中双击“确定”按钮进入该按钮代码编写状态以完成最终的更新代码实现,完整代码如下。
protected void btnConfirm_Click(object sender, EventArgs e) { SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=PC-200807311524;uid=sa; pwd=sa;database=studentDB"; conn.Open(); string ID = hfID.Value.ToString(); string Name = txtName.Text.Trim(); string Sex = rBtnSex.SelectedValue.ToString(); string City = ddlCity.SelectedValue.ToString(); string sqlString = "update student set name=’" + Name + "’, sex=’" + Sex + "’,city=’" + City + "’ where id=" + ID; SqlCommand cmd = new SqlCommand(sqlString, conn); if (cmd.ExecuteNonQuery() == -1) { //更新失败 Response.Write("更新失败"); } else { //更新成功 Response.Write("更新成功"); } conn.Close(); conn.Dispose();
}
⑤然后按Ctrl+F5组合键,首次运行Update.aspx页面显示效果如图4-18所示。因为直接运行没有给Update.aspx页面传递ID值,所以重定向到StudyCommand003.aspx页面。
⑥在图4-18中选择编号2然后单击“更新”按钮,将跳转到Update.aspx页面,显示效果如图4-19所示。
⑦在图4-19中比如把姓名修改为“王辉”,性别修改为“男”,城市修改为“天津”,然后单击“确定”按钮,显示效果如图4-20所示。
⑧ 此时如果再选择运行StudyCommand001.aspx,运行显示效果如图4-21所示。
4.使用Command对象的ExecuteScalar()方法
命令对象的ExecuteScalar()方法用于执行查询,并返回查询所返回的结果集中第一行的第一列。所有其他的列和行将被忽略。使用 ExecuteScalar方法从数据库中检索单个值(例如一个聚合值)。与使用ExecuteReader方法然后使用 DbDataReader返回的数据执行生成单个值所需的操作相比,此操作需要的代码较少。
比如要通过代码获取student表中的总记录数就可以使用该方法完成,完整代码如下。
SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=DUANKEQI;uid=sa;pwd=sa; database=studentDB"; conn.Open(); string sqlString = "select count(*) from student"; SqlCommand cmd = new SqlCommand(sqlString, conn); string count = cmd.ExecuteScalar().ToString(); Response.Write("学生表student中共有" + count + "条记录"); conn.Close(); conn.Dispose();
5.使用Command对象操作无参数存储过程
在开发各种数据库应用程序过程中,对于一些复杂的数据处理总会用到存储过程,本小节详细介绍使用Command对象操作无参数存储过程的方法。首先需要编写一个数据库存储过程,接下来给出一个示例存储过程代码。
use studentDB go create procedure testProc as begin select * from student where city='北京' end --execute testProc
(1)在ASP.NET网站项目StudyCommand中添加页面StudyCommand004.aspx。在页面StudyCommand004.aspx的设计窗口添加GridView控件布局信息显示的容器。然后进入StudyCommand004.aspx的代码窗口编写Page_Load事件代码。Page_Load事件完整代码如下。
protected void Page_Load(object sender, EventArgs e) { SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=DUANKEQI;uid=sa;pwd=sa; database=studentDB"; conn.Open(); //实例化一个命令对象 SqlCommand cmd = new SqlCommand(); //指定命令对象的命令类型为存储过程 cmd.CommandType = CommandType.StoredProcedure; //指定存储过程名称 cmd.CommandText = "testProc"; //指定命令对象使用的连接对象 cmd.Connection = conn; SqlDataReader sdr = cmd.ExecuteReader(); GridView1.DataSource = sdr; GridView1.DataBind(); conn.Close(); conn.Dispose(); }
(2)然后按Ctrl+F5组合键,运行显示效果如图4-22所示。
4.3.4 Parameter
Parameter类是用来给所有数据源控件提供一个绑定到应用程序变量、用户标识和选择以及其他数据的机制。该类作为所有ASP.NET参数类型的基类。在具体应用时对于不同的数据提供程序写法不同,程序的参数思想是等价的,接下来给出几个基本的定义语法。
SqlParameter para1 = new SqlParameter(); OdbcParameter para2 = new OdbcParameter(); OleDbParameter para3 = new OleDbParameter(); OracleParameter para4 = new OracleParameter();
本小节主要使用SQL Server数据库完成操作,当然就使用SQL数据提供程序SqlClient,其他提供程序和SQL数据提供程序SqlClient的使用方法类似。
1.SqlParameter的定义方法
完整参数定义格式一: //实例化一个参数对象 SqlParameter para1 = new SqlParameter(); //定义参数名称 para1.ParameterName = "@Name"; //定义参数的取值 para1.Value = "张三"; //定义参数的类型 para1.TypeName = SqlDbType.VarChar.ToString(); //定义参数数据类型的尺寸,以字节为单位 para1.Size = 20; //定义参数的方向,输入Input或输出Output //或输入输出InputOutput获返回值ReturnValue //默认没有指定取值为Input,即为输入参数 para1.Direction = ParameterDirection.Input; 完整参数定义格式二: //实例化一个参数对象(2) SqlParameter para2 = new SqlParameter("@Name", "张三"); 完整参数定义格式三: //实例化一个参数对象(3) SqlParameter para3 = new SqlParameter("@Name", SqlDbType.VarChar); para3.Value = "张三"; para3.Size = 20; para3.Direction = ParameterDirection.Input; 完整参数定义格式四: //实例化一个参数对象(4) SqlParameter para4 = new SqlParameter("@Name", SqlDbType.VarChar, 20); para4.Value = "张三"; para4.Direction = ParameterDirection.Input;
实际上上面4种写法原理都是完全等价,只不过方法一把所有参数对象的属性都分别定义,而后面的3种方法是在实例化一个参数对象时顺便完成了对应参数对象部分相关属性的定义。在实际开发过程中可以选择使用,相对来讲方法二使用更方便一些,虽然没有指定类型、大小和参数方向,但默认就是字符串且为输入参数,所以可以不用指定。
这里的定义语法虽然简单,但是一定要理解其中的含义,这正是为接下来的参数操作做技术铺垫,虽然属于基础内容但是很重要。
2.使用SqlParameter完成输入参数操作
首先需要编写一个带有输入参数的数据库存储过程,接下来给出一个示例存储过程myproc001的代码。
use studentDB go create procedure myproc001 ( @city varchar(20), @sex char(2) ) as begin select * from student where city=@city and sex=@sex end --execute myproc001 ‘北京','男'
(1)在ASP.NET网站项目StudyCommand中添加页面StudyCommand Parameter001.aspx。在页面StudyCommandParameter001.aspx的设计窗口添加GridView控件布局信息显示的容器。然后进入StudyCommandParameter001.aspx的代码窗口编写Page_Load事件代码。接下来给出Page_Load事件完整代码。
protected void Page_Load(object sender, EventArgs e) { string City = "北京"; string Sex = "男"; SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=DUANKEQI;uid=sa;pwd= sa;database=studentDB"; conn.Open(); SqlCommand cmd = new SqlCommand(); cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "myproc001"; cmd.Connection = conn; //定义两个参数对象 SqlParameter paraCity = new SqlParameter("@city", City); SqlParameter paraSex = new SqlParameter("@sex", Sex); //把定义好的参数对象添加到命令对象cmd中 cmd.Parameters.Add(paraCity); cmd.Parameters.Add(paraSex); SqlDataReader sdr = cmd.ExecuteReader(); GridView1.DataSource = sdr; GridView1.DataBind(); sdr.Close(); sdr.Dispose(); conn.Close(); conn.Dispose(); }
(2)然后按Ctrl+F5组合键,运行显示效果如图4-23所示。
3.使用SqlParameter完成输入、输出参数操作
首先需要编写一个带有输入参数和输出参数的数据库存储过程,接下来给出一个示例存储过程myproc002代码。
use studentDB go create procedure myproc002 ( @city varchar(20), @sex char(2), @count int output ) as begin select @count=count(*) from student where city=@city and sex=@sex end --declare @n int --execute myproc002 '北京','男',@n output --print @n
(1)在ASP.NET网站项目StudyCommand中添加页面StudyCommand Parameter002.aspx。在页面StudyCommandParameter002.aspx的设计窗口添加GridView控件布局信息显示的容器。然后进入StudyCommandParameter002.aspx的代码窗口编写Page_Load事件代码。接下来给出Page_Load事件完整代码。
protected void Page_Load(object sender, EventArgs e) { string City = "北京"; string Sex = "男"; string Count = string.Empty; SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=DUANKEQI;uid=sa;pwd=sa; database=studentDB"; conn.Open(); SqlCommand cmd = new SqlCommand(); cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "myproc002"; cmd.Connection = conn; //定义参数对象 SqlParameter paraCity = new SqlParameter("@city", City); SqlParameter paraSex = new SqlParameter("@sex", Sex); //把定义好的参数对象添加到命令对象cmd中 cmd.Parameters.Add(paraCity); cmd.Parameters.Add(paraSex); //定义输出参数 SqlParameter paraCount = new SqlParameter("@count", SqlDbType.Int); paraCount.Direction = ParameterDirection.Output; //把定义好的输出参数对象添加到命令对象cmd中 cmd.Parameters.Add(paraCount); cmd.ExecuteNonQuery(); //获取输出参数的值 Count = cmd.Parameters["@count"].Value.ToString(); Response.Write(Count.ToString()); conn.Close(); conn.Dispose(); }
(2)然后按Ctrl+F5组合键,运行显示效果如图4-24所示。
4.3.5 DataReader
DataReader对象是由Command对象执行ExecuteReader方法后生成的一个只读的数据集,有点类似于ASP环境中的RecordSet对象。一般对于Command命令执行查询生成的记录集的简单输出都使用Command.ExecuteReader()方法生成DataReader对象后再使用DataReader对象的属性方法处理输出。而DataReader对象生成的记录集是一个只读、只向前的记录集。若要对生成的记录集作复杂的处理如分页、排序、更新、删除等,就必须使用DataAdapter对象结合DataSet对象来完成。这在后续章节将详细讲解。
DataReader对象的常用属性和方法如表4-7所示。
经常使用的属性和方法有FieldCount、HasRows、DataReader[“字段名称”]、DataReader[索引号]、Read、Close、Dispose。接下来举例说明其使用方法。
下面的代码主要是熟悉FieldCount、HasRows属性的用法。
SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=DUANKEQI;uid=sa;pwd=sa; database=studentDB"; conn.Open(); string sqlString = "select * from student order by id asc"; SqlCommand cmd = new SqlCommand(sqlString,conn); SqlDataReader sdr = cmd.ExecuteReader(); if (sdr.HasRows) { Response.Write("存在记录<br />"); Response.Write("字段数:" + sdr.FieldCount.ToString() + "<br />"); } else { Response.Write("不存在记录"); } sdr.Close(); sdr.Dispose(); conn.Close(); conn.Dispose();
如果student(id,name,sex,city)表中存在至少一条记录,那么就输出“存在记录字段数:4”,否则就输出“不存在记录”。HasRows属性返回的是bool类型的值,如果返回true就表示存在,如果返回false就表示不存在。FieldCount属性直接返回DataReader对象所代表记录集的字段数。
当然判断DataReader对象所代表记录集是否存在记录除了使用HasRows属性外,还可以使用Read()方法完成,如果使用Read方法就可以使用如下的代码完成。
SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=DUANKEQI;uid=sa;pwd= sa;database=studentDB"; conn.Open(); string sqlString = "select * from student order by id asc"; SqlCommand cmd = new SqlCommand(sqlString,conn); SqlDataReader sdr = cmd.ExecuteReader(); if (sdr.Read()) { Response.Write("存在记录<br />"); Response.Write("字段数:" + sdr.FieldCount.ToString() + "<br />"); } else { Response.Write("不存在记录"); } sdr.Close(); sdr.Dispose(); conn.Close(); conn.Dispose();
默认情况下,获取到一个DataReader对象后,记录指针在第一条记录之前,若要获取第一条记录信息,必须先使用Read()方法读取,一般常使用该方法作为循环处理的条件。接下来的代码就是使用DataReader对象的Read()方法、DataReader[“字段名称”]属性结合第2章讲解的Web Table对象完成一个完整的表信息输出。这种输出的好处是不受GridView控件格式的限制,用户可以人为定义输出的样式。
接下来给出完整编程步骤和代码。
(1)新建ASP.NET网站项目StudyDataReader,删除默认Default.aspx页面,添加页面StudyDataReader002.aspx。在页面StudyData Reader002.aspx的设计窗口添加Web Table控件(在工具箱的“标准”选项卡中)作为显示数据信息的容器,页面设计窗口的布局如图4-25所示。
(2)然后直接进入StudyDataReader002.aspx的代码窗口中编写Page_Load事件初始化代码来完成对页面上的Web Table信息的填充。接下来给出完整代码。
protected void Page_Load(object sender, EventArgs e) { #region //定义表格Table1的通用属性 Table1.CellPadding = 2; Table1.CellSpacing = 0; Table1.Width = 400; Table1.Style.Add("font-size", "12px"); //row 1 定义第一行 TableRow tr = new TableRow(); tr.BackColor = System.Drawing.ColorTranslator. FromHtml("#CCCCCC"); tr.BorderColor = System.Drawing.Color.Blue; tr.BorderStyle = BorderStyle.Solid; tr.BorderWidth = 1; tr.Style.Add("text-align", "center"); tr.Style.Add("font-weight", "bold"); tr.Height = 25; //cell 1 TableCell tc = new TableCell(); tc.Width = 200; tc.Text = "编号"; tc.BorderColor = System.Drawing.Color.Blue; tc.BorderStyle = BorderStyle.Solid; tc.BorderWidth = 1; tr.Cells.Add(tc); //cell 2 tc = new TableCell(); tc.Width = 200; tc.Text = "姓名"; tc.BorderColor = System.Drawing.Color.Blue; tc.BorderStyle = BorderStyle.Solid; tc.BorderWidth = 1; tr.Cells.Add(tc); //cell 3 tc = new TableCell(); tc.Width = 200; tc.Text = "性别"; tc.BorderColor = System.Drawing.Color.Blue; tc.BorderStyle = BorderStyle.Solid; tc.BorderWidth = 1; tr.Cells.Add(tc); //cell 4 tc = new TableCell(); tc.Width = 200; tc.Text = "城市"; tc.BorderColor = System.Drawing.Color.Blue; tc.BorderStyle = BorderStyle.Solid; tc.BorderWidth = 1; tr.Cells.Add(tc); Table1.Rows.Add(tr); #endregion #region 定义具体数据行 SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"server=DUANKEQI;uid=sa;pwd=sa ;database=studentDB"; conn.Open(); string sqlString = "select id,name,sex,city from student order by id asc"; SqlCommand cmd = new SqlCommand(sqlString, conn); SqlDataReader sdr = cmd.ExecuteReader(); if (sdr.HasRows)//如果存在数据信息就准备输出 { while (sdr.Read())//通过循环的方式输出所有信息 { //row 1 定义第一行 tr = new TableRow(); tr.BackColor = System.Drawing.ColorTranslator. FromHtml("#FFFFFF"); tr.BorderColor = System.Drawing.Color.Blue; tr.BorderStyle = BorderStyle.Solid; tr.BorderWidth = 1; tr.Height = 25; //cell 1 tc = new TableCell(); tc.Width = 200; tc.Text = sdr["id"].ToString(); tc.BorderColor = System.Drawing.Color.Blue; tc.BorderStyle = BorderStyle.Solid; tc.BorderWidth = 1; tr.Cells.Add(tc); //cell 2 tc = new TableCell(); tc.Width = 200; tc.Text = sdr["name"].ToString(); tc.BorderColor = System.Drawing.Color.Blue; tc.BorderStyle = BorderStyle.Solid; tc.BorderWidth = 1; tr.Cells.Add(tc); //cell 3 tc = new TableCell(); tc.Width = 200; tc.Text = sdr["sex"].ToString(); tc.BorderColor = System.Drawing.Color.Blue; tc.BorderStyle = BorderStyle.Solid; tc.BorderWidth = 1; tr.Cells.Add(tc); //cell 4 tc = new TableCell(); tc.Width = 200; tc.Text = sdr["city"].ToString(); tc.BorderColor = System.Drawing.Color.Blue; tc.BorderStyle = BorderStyle.Solid; tc.BorderWidth = 1; tr.Cells.Add(tc); Table1.Rows.Add(tr); }//end while }//end if sdr.Close(); sdr.Dispose(); conn.Close(); conn.Dispose(); #endregion }
(3)按Ctrl+F5组合键,运行显示效果如图4-26所示。
4.3.6 连接池的应用
连接池操作可以显著提高应用程序的性能和可缩放性。本节将详细介绍SQL Server连接池 (ADO.NET)的操作。
连接到数据库服务器通常由几个需要很长时间的步骤组成。必须建立物理通道(例如套接字或命名管道),必须与服务器进行初次握手,必须分析连接字符串信息,必须由服务器对连接进行身份验证,必须运行检查以便在当前事务中登记,等等。
实际上,大多数应用程序仅使用一个或几个不同的连接配置。这意味着在执行应用程序期间,许多相同的连接将反复地打开和关闭。为了使打开的连接成本最低,ADO.NET使用称为连接池的优化方法来完成。
连接池减少新连接需要打开的次数。池进程保持物理连接的所有权。通过为每个给定的连接配置保留一组活动连接来管理连接。只要用户在连接上调用 Open,池进程就会检查池中是否有可用的连接。如果某个池连接可用,会将该连接返回给调用者,而不是打开新连接。应用程序对该连接调用Close时,池进程会将连接返回到活动连接池集中,而不是真正关闭连接。连接返回到池中之后,即可在下一个Open调用中重复使用。
简单来讲,连接池就是一个放着连接的池子,就像水池里放的是水,连接池里存放了多个数据库连接对象:Connection,而且这个池子当中的每个连接对象,都保持不销毁,不断开的状态。用户请求时需要数据库连接,就从连接池里抓一个连接来用,用完放回连接池即可。
池连接可以显著提高应用程序的性能和可缩放性。默认情况下,ADO.NET中启用连接池。除非显式禁用,否则,连接在应用程序中打开和关闭时,池进程将对连接进行优化。另外在ADO.NET中的连接池大小还可以通过数据库连接字符串来控制,例如:
string connStr = "Server=MydbServerName;User ID=sa;Password=sa;Initial Catalog=Mydb;Pooling=True;Min Pool Size=5;Max Pool Size=10; Persist Security Info=False;";
连接字符串中各个参数含义如下。
(1)Server表示要连接的数据库服务器名称。
(2)User ID表示登录SQL数据库服务器使用SQL验证的用户名称,也可以使用uid。
(3)Password表示登录SQL数据库服务器使用SQL验证的登录口令,也可以使用pwd。
(4)Initial Catalog表示要操作的数据库名称,也可以使用database。
(5)Pooling表示是否打开连接池,默认为打开,关掉时需要Pooling = False。
(6)Min Pool Size表示连接池最少保存几个连接对象,默认为0。
(7)Max Pool Size表示连接池最多保存几个连接对象,默认为100。
(8)Persist Security Info关键字的默认设置为false。如果将其设置为true或yes,则允许在打开连接后通过连接获取安全敏感信息(包括用户ID和密码)。保持将Persist Security Info设置为false,以确保不受信任的来源不能访问敏感的连接字符串信息。
但一定要注意,连接字符串的任何改动,系统都会认为是另一个完全不同的数据库连接,将会创建新的连接池,这必然会造成更大的系统开销。所以,为了保证某些连接对象属于一个连接池,连接字符串不能有任何变化,包括大小写、空格,都不能有任何变化。
通常在某一个数据库应用程序中总是连接某一个数据库,此时经常把连接字符串写在配置文件web.config中,在需要的地方直接调用,下面给出在web.config文件中定义连接字符串的方法。在某个网站程序根目录下可以直接找到web.config文件( 一般情况下用户每创建一个网站程序系统就会自动在网站程序根目录下生成一个web.config文件),然后打开web.config文件,在其中找到<appSettings/>项目修改,如图4-27所示,被选中的部分就是添加的代码。
当然按照此方法还可以在web.config文件中定义其他常用的固定字符串信息。图4-27中key=connStr的value完整字符串如下:
Server=DUANKEQI;User ID=sa;Password=sa;Initial Catalog=studentDB;Poo ling=true;Min Pool Size=5;Max Pool Size=10; Persist Security Info=False;
在程序页面中可以使用如下代码获取该字符串:
string connStr = System.Configuration.ConfigurationManager. AppSettings["connStr"].ToString();
如果程序页面上方已经导入了“System.Configuration”命名空间,则代码也可以简化成为:
string connStr = ConfigurationManager.AppSettings["connStr"].ToString();
共有条评论 网友评论