一、先了解如下两张官方的图解
图1
图2
以上这两张图都是描述Ado.NET的工作原理。
1.应用程序读取数据共有三种方式:
(1)直接通过Command对象读取(但一次只能对一条数据,即表的一行),读完后不断开连接。
(2)通过DataReader对象读取(类似单向管道似的,一条一条的读数据),读完后不断开连接。
(3)通过DataAdapter一次读一堆数据(这个堆就是DataSet),读完后就断开数据库连接了。
2.应用程序更新和插入数据共有两种方式:
由于DataReader是单向的,所有不能用于插入和更新数据,只能从数据库读取数据,所以应用程序更新和插入数据只有上面的(1)和(3)两者方式。
从Ado.Net的工作原理图可以看出,不管用哪种方式和数据库打交道,都必须有三个的东西:Connection,Command和Sql语句。
二、具体工作原理请参照下面图解:
第(1)种方式如下图:
图(1)
图中把工厂比喻成数据库,把商城仓库比喻成应用程序所在内存,把Command比喻成取货员“二愣子”(因为他比较笨),把Sql语句比喻成订单,把桥比喻成数据库连接对象。
第(1)方式的工作过程就是,首先要j架设好桥,然后二愣子拿到订单(Sql语句)后,就往工厂跑,把订单给工厂管理员,取回订单所需的货物(数据)后,再折返回来把货物放入仓库(内存)。但是由于一个人的力量有限,二愣子来回跑的过程中,一个来回只能搬回一件货物。这时候连接不能断(桥不能拆),因为每次需要取一件货物的时候,二愣子都需要来回跑。
方式(1)的总体印象就是,“二愣子”不断的来回跑。
第(2)种方式的工作过程就是,架设好桥后,二愣子拿着“订单”往工厂跑,但这次的订单比较复杂,他相当于一份合同,要求和工厂建立一个管道,仓库需要什么货物,直接操作管道的一头的控制按钮,则管道另一头会自动提取需要的货物并通过管道输送到仓库。这样“二愣子”就只需要跑一趟就行了(这份工作轻松、高兴!)。这时候连接也不能断(管道不能断),只有保持连接才能不断的一件件从工厂取货物并通过管道输送到仓库。
方式(2)的总体印象就是,有一天单向管道。
如下图:
图(2)
第(3)种方式的工作过程就是,架设好桥后,二愣子拿着“订单”开着车往工厂跑,但这次订单要求的货物比较多,工厂就把这一堆货物帮二愣子装上车,二愣子高高兴兴的开着车兜着风把这一堆货物运回去。这次二愣子不仅能兜风,而且也只跑一趟(这份工作真拉风!)。车开回来后就可以断开连接了(完全可以把桥拆了),因为需要的所有货物都已经通过货车运回来了。此时,货车就比喻为DataAdapter对象,DataSet对象就比喻成货车拉回来的那“一堆货物的集合”。
方式(3)的总体印象就是,“二愣子”开着货车。
见下图:
图(3)
另外,网上有一个更好看点的图(意义和上图一样),如下:
总结:从上面的图可以看出,无论用什么方式取货物都必须有三个东西参与才能完成:桥,二愣子,订单。即:Connection,Command和Sql语句。实际上在使用DataAdapter的时候可能看不到Command对象,其实DataAdapter也是必须通过Command对象取货物的,只不过DataAdapter内部自己有Command对象而已,DataAdapter使用自己内部的Command对象(这点从最上面的Ado.net原理图可以看出)。
取货物就是从数据库中取数据,更新和插入数据到数据库的过程和取数据一样,也需要Connection,Command和Sql语句这三个东西,此不赘述。
具体代码如下:
假设配置文件如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="Northwind"
providerName ="System.Data.SqlClient"
connectionString ="server=(local); integrated security = SSPI;
database = Northwind" />
</connectionStrings>
</configuration>
(1)直接通过Command对象读取(但一次只能对一条数据,即表的一行),读完后不断开连接。
第一步:建立Connection对象(桥)
string connectionString = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString.ToString(); //从配置文件中取出连接字符串
SqlConnection conn = new SqlConnection(connectionString); conn.Open(); //打开连接
第二步:建立SQL语句(订单)
string sqlStr=“ Select * from Student where ID=’10’ ”;
第三步:建立Command对象(二愣子),用这个对象直接返回一个行数据。
SQLCommand cmd=new SQLCommand(sqlStr,conn); //参数就是sql语句和连接对象
//注意通过new Command对象的连个参数把这三个必须的东西联系在一起了。
下面就是执行Command对象的方法了,共有三种方法:
int LineCount=cmd.ExecuteNonQuery(); //一般用于更新和插入语句,不返回查询结果,但这个函数的返回值本身返回影响的行数。
DataReader dr=cmd.ExecuteReader(); //一般用于Select语句返回查询的数据,然后通过DataReader一个个的读取。【建立管道】
int OneValue=cmd.ExecuteScaler(); //一般用于Select查询,但返回结果为一个值的情况,比如SQL语法中的Count函数和Sum函数。这也印证了二愣子力量有限,仅凭自己的力量一次只能搬回一件货物,或者不搬回货物,仅通知数据库更新和插入一个数据。
总之,更新和插入用ExecuteNonQuery,查询主要使用ExecuteReader,特殊情况(只返回单个值)才用ExecuteScaler。
当然,直接用Command对象读取数据就只有ExecuteNonQuery和ExecuteScaler两种方法,建立管道方法属于下面(2)的方法。
最后别忘了关闭连接,conn.Close();
(2)通过DataReader对象读取(类似单向管道似的,一条一条的读数据),读完后不断开连接。
第一步:建立Connection对象(桥)
string myconn=”Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind”;
SqlConnection conn=new SqlConnection(myconn);
第二步:建立SQL语句(订单)
string MySQL=”select OrderID,CustomerID from Orders where CustomerID=’CHOPS’”;
第三步:建立Command对象(二愣子),但用这个对象返回一个管道DataReader
SqlCommand cmd=new(mysql,conn); conn.Open(); //打开数据库连接,用之前打开就行,放在哪无所谓
//通过SqlCommand的ExecuteReader()方法构造DataReader 对象。
SqlDataReader myreader=cmd.ExecuteReader();
while(myreader.read()){
Console.WriteLine(myreader.GetInt32(0)+”,”+myreader.GetString(1));
}
myreader.Close();
myconnection.Close();
补充:SqlDataReader读数据方法有两大类,带小括号的函数和带中括号的索引属性
带小括号的有,GetInt32(), GetString()等,参数都是int类型,表示列号,也只能使用列号。
带中括号的有,直接带上中括号,有myreader[0] 和 myreader[“ID”]两种,即可以使用列号,也可以使用列名。
(3)通过DataAdapter一次读一堆数据(这个堆就是DataSet),读完后就断开数据库连接了。
第一步:建立Connection对象(桥)
string connectionString = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString.ToString(); //从配置文件中取出连接字符串
SqlConnection conn = new SqlConnection(connectionString); conn.Open(); //打开连接
第二步:建立SQL语句(订单)
string sqlStr=“ Select * from Student where ID=’10’ ”;
第三步:建立DataAdapter对象(这个对象内部隐式的建立了Command对象),即建立好货车就行了,货车内部有人。但通过这个对象填充DataSet,DataSet在本地内存中
SqlDataAdapter da=new SqlDataAdapter(sqlStr,conn); //参数仍然是sql语句和连接对象
DataSet ds=new DataSet();
ds.Fill(ds,”表名”);
使用参数的Sql语句,即使用Paramerter对象例子如下:
这里使用SqlParamerter对象,这个对象是SqlParamerters集合的子元素,每个Command对象都有一个Parameters属性。【就好像二愣子不仅拿着订单,还拿着订单中每样货物对应的更详细的货物描述的表单。】
下面代码目的是根据已知的名字和密码获取年龄大小。
string name = “Tim”;
string pwd =”123456”;
string Age;
string constr = @”server=.\sqlexpress;database=MyDataBase;uid=sa;pwd=sa”;
using (SqlConnection sqlconn = new SqlConnection(constr)) //第一步,建立连接对象
{
//参数化查询1
string cmdstr = @”select age from loginn where uname=@name1 and upwd=@pwd1”;
//第二步,建立Sql语句。 @name1,@pwd1仅仅是别名而已,相当于局部变量
using (SqlCommand cmd =new SqlCommand(cmdstr, sqlconn)) //第三步,建立Command对象
{
//加参数
cmd.Parameters.Add(new SqlParameter(”@name1”, name));
//把name中的值赋给@name
cmd.Parameters.Add(new SqlParameter(”@pwd1”, pwd));//注意Add方法中是new了一个 SqlParameter
// cmd.Parameters.AddWithValue(“@pwd1”, pwd);//此方法内部实现同Add,推荐用前者
if (sqlconn.State == ConnectionState.Closed)
{
sqlconn.Open();
Age=cmd.ExecuteScalar(); //获取年龄
//由于是查询且age字段是int类型,且Sql语句返回一个单值,所以用ExecuteScalar最方便。 否则就需要用ExecuteReader。
} //end if
} //end using
} //end using
使用存储过程的例子如下:
转自:
ADO.NET中调用存储过程
此时,存储过程就看成一种特色类型的订单,也就是通知二愣子手里拿的订单是个“存储过程”类型的订单,另外这种订单也需要“更详细的货物描述的表单”,所以也需要参数,其他的就和普通带参数的订单的执行过程一模一样了,需要注意的是一般使用Command对象的ExecuteNonQuery方法执行存储过程。
下面这个例子展示如何在ADO.NET调用存储过程。我只选择了一种方式,说实话不太喜欢多样化的方式去处理问题,这种发散式的做法在编程中似乎没有必要。选择自己喜欢的一种方式。
这里SQL Server有一个样本数据库Northwind.
以Northwind里的Region表为例:
Region表有2个字段:
- RegionID, int 主键
- RegionDescription, nchar(50)
写以下几个存储过程:
RegionUpdate: 多个参数
CREATE PROCEDURE RegionUpdate(@RegionID INTEGER, @RegionDescription NCHAR(50))
AS
SET NOCOUNT OFF
UPDATE Region
SET RegionDescription = @RegionDescription
WHERE RegionID = @RegionID
GO
RegionDelete 一个参数
CREATE PROCEDURE RegionUpdate(@RegionID INTEGER)
AS
SET NOCOUNT OFF
DELETE FROM Region
WHERE RegionID = @RegionID
GO
RegionInsert 带返回参数
CREATE PROCEDURE RegionInsert(@RegionDescription NCHAR(50), @RegionID INTEGER OUTPUT)
AS
SET NOCOUNT OFF
SELECT @RegionID = MAX(RegionID) + 1
FROM Region
INSERT INTO Region VALUES(@RegionID, @RegionDescription)
GO
工程添加下面的配置文件
App.configuration
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="Northwind"
providerName ="System.Data.SqlClient"
connectionString ="server=(local); integrated security = SSPI;
database = Northwind" />
</connectionStrings>
</configuration>
下面是C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
namespace SQLProcedureDemo
{
class Program
{
private static string GetConnectionStringsConfig(string connectionName)
{
string connectionString = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString.ToString();
Console.WriteLine(connectionString);
return connectionString;
}
static void Main(string[] args)
{
string source = GetConnectionStringsConfig("Northwind");
try
{
using (SqlConnection conn = new SqlConnection(source))
{
conn.Open();
// Invoke RegionUpdate Procedure
SqlCommand cmd = new SqlCommand("RegionUpdate", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter updatepara1 = new SqlParameter("@RegionID", SqlDbType.Int);
updatepara1.Value = 3;
cmd.Parameters.Add(updatepara1);
SqlParameter updatepara2 = new SqlParameter("@RegionDescription", SqlDbType.NChar);
updatepara2.Value = "Northern";
cmd.Parameters.Add(updatepara2);
// You can also use the following statement
//cmd.Parameters.AddWithValue("@RegionID", 3);
//cmd.Parameters.AddWithValue("@RegionDescription", "Northern");
cmd.ExecuteNonQuery();
// Invoke RegionDelete Procedure
SqlCommand cmdDel = new SqlCommand("RegionDelete", conn);
cmdDel.CommandType = CommandType.StoredProcedure;
SqlParameter myParameter = new SqlParameter("@RegionID", SqlDbType.Int);
myParameter.Value =5;
cmdDel.Parameters.Add(myParameter);
cmdDel.ExecuteNonQuery();
// Invoke RegionInsert Procedure
SqlCommand cmdInsert = new SqlCommand("RegionInsert", conn);
cmdInsert.CommandType = CommandType.StoredProcedure;
SqlParameter para1 = new SqlParameter("@RegionDescription", SqlDbType.NChar);
para1.Value = "South West";
cmdInsert.Parameters.Add(para1);
SqlParameter para2 = new SqlParameter("@RegionID", SqlDbType.Int);
para2.Direction = ParameterDirection.Output;
cmdInsert.Parameters.Add(para2);
cmdInsert.ExecuteNonQuery();
int newRegionID = (int)cmdInsert.Parameters["@RegionID"].Value;
Console.WriteLine(newRegionID);
conn.Close();
}
}
catch (SqlException ex)
{
//
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}