asp.net学习之ado.net(连接模式访问)
?? ado.net框架支持兩種模式的數(shù)據(jù)訪問: 連接模式(Connected)和非連接模式(disconnected)。這一節(jié)介紹如何使用連接模式訪問數(shù)據(jù)庫中的數(shù)據(jù),利用ADO.NET中的Connection,Command,DataReader來獲取和修改數(shù)據(jù)庫中的數(shù)據(jù)
1. 連接模式的數(shù)據(jù)訪問
???? 連接模式要使用到的三個核心類:
???? ● IDBConnection : 表示數(shù)據(jù)源的連接,所有Connection類的基類
??????? SqlConnection實現(xiàn)了IDBConnection接口,用來與SQL Server數(shù)據(jù)源進行連接
???? ● DBCommand? : 表示所有Command類的基類
??????? SqlCommand實現(xiàn)了IDBCommand接口,與來對SQL Server數(shù)據(jù)庫執(zhí)行的一個 Transact-SQL 語句或存儲過程
???? ● DataReader : 所有DataReader類的基類
??????? SqlDataReader實現(xiàn)了IDataReader接口,提供一種從 SQL Server 數(shù)據(jù)庫讀取行的只進流的方式。
???? 如果要連接到微軟SQL Server數(shù)據(jù)庫,盡量使用SqlClient命名空間中的SqlConnection,SqlCommand,SqlDataReader類,如果與Oracle數(shù)據(jù)庫通信,應該使用OracleClient命名空間的類;與其它數(shù)據(jù)庫進行通信,就應該使用OleDB或ODBC命名空間的類。
圖1: DbConnection與DbCommand的關系圖如下所示:
例1: 一個簡單的連接數(shù)據(jù)庫查詢的例子
=== App_Code\DawnDataObject.cs ===
namespace? DawnDataObject
{
??? public?class Movies? // 數(shù)據(jù)實體對象
??? {
??????? public?static?readonly?string _connectionString;? // 連接數(shù)據(jù)庫字符串為靜態(tài)成員,每個實例共享。
??????? static Movies(){
??????????? _connectionString = WebConfigurationManager.ConnectionStrings["DawnEnterpriseDBConnectionString"].
??????????????? ConnectionString;
??????? }
??????? private?string _title;
??????? private?string _director;
??????? // Movies類中包括的屬性有Title、Director
??????? public?string Title{
??????????? get { return _title; }
??????????? set { _title = value; }
??????? }
??????? public?string Director {
??????????? get { return _director; }
??????????? set { _director = value; }
??????? }
??????? // Movies類中的GetAll方法返回一個List對象,該對象可以被GridView等控件做為數(shù)據(jù)源綁定
??????? public List<Movies> GetAll()
??????? {
??????????? List<Movies> result =?new List<Movies>();
??????????? SqlConnection conn =?new SqlConnection(_connectionString);
??????????? SqlCommand comm =?new SqlCommand("select Title,Director from Movies", conn);
??????????? using(conn){? // using關鍵字指定了conn一旦離開這個代碼段,自動調(diào)用其Dispose函數(shù)
??????????????? conn.Open();
??????????????? SqlDataReader reader = comm.ExecuteReader();
??????????????? while(reader.Read()){
??????????????????? Movies newmovie =?new Movies();
??????????????????? newmovie._title = (string)reader["Title"];
??????????????????? newmovie._director = (string)reader["Director"];
??????????????????? result.Add(newmovie);
??????????????? }
??????????????? return result;
??????????? }
??????? }
??? }
}
=== Movies.aspx ===
<asp:ObjectDataSource ID="ObjectDataSource1" TypeName="DawnDataObject.Movies" SelectMethod="GetAll" runat="server"?/>
2. 使用Connection對象
?? Connection對象表示數(shù)據(jù)源的連接,實例化Connection對象時,需要向構(gòu)造函數(shù)傳遞一個連接字符串。連接字符串包含了連接到數(shù)據(jù)源所需要的位置和安全認證信息
?? Connection對象也提供了相應的方法對數(shù)據(jù)庫進行打開和關閉操作;提供了相應的屬性確認數(shù)據(jù)庫的狀態(tài)。
?? 2.1 連接字符串
??????? 一個最普通的連接字符串如下所示:
??? SqlConnection conn =?new SqlConnection(_connectionSring);?? // 可以在建立SqlConnection對象時把連接字符串傳遞給構(gòu)造參數(shù)
?????? 也可以使用Connection對象的ConnectionString屬性來獲取或設置用于打開 SQL Server 數(shù)據(jù)庫的字符串
??? conn.ConnectionString =?"Persist Security Info=False;Integrated Security=true;Initial Catalog=Northwind;server=(local)";
?????? ado.net提供了相應的DbConnectionStringBuilder類來管理數(shù)據(jù)庫連接字符串。相對應的,sqlClient命名空間中就包含了一個SqlConnectionStringBuilder類。
例2:使用SqlConnectionStringBuilder類管理數(shù)據(jù)庫連接字符串
connstrBuilder.DataSource =?"(local)";
connstrBuilder.InitialCatalog =?"Test";
connstrBuilder.IntegratedSecurity =?true;
using(SqlConnection testConn =?new SqlConnection(connstrBuilder.toString()))
{
??????? testConn.open();
??????? if (testConnection.State == ConnectionState.Open) {
???????????? Console.WriteLine("Connection successfully opened");
??????? }
}
?????? 可以把ConnectionString保存在Web.config文件中,然后在程序中使用WebConfigurationManager類進行讀取
??? <add connectionString="Data Source=.;Initial Catalog=DawnEnterpriseDB;User ID=sa;Password=******" name="DawnEnterpriseDBConnectionString" providerName="System.Data.SqlClient"?/>
</configuration>
?????? 如何讀取其中連接字符串的話在例1中有示例
??? 2.2 IDbConnection的共通行為與屬性
???????? 因為相關的SqlConnection,OracleConnection,OleDBConnection與ODBCConnection都要實現(xiàn)IDBConnection接口,該接口規(guī)定了Connection類必須實現(xiàn)的一些行為和屬性
???????? 2.2.1: 相關方法
???????????? ● BeginTransaction() : 開始數(shù)據(jù)庫事務。
???????????? ● ChangeDatabase(string database) : 更改當前數(shù)據(jù)庫。
???????????? ● Open() : 打開一個數(shù)據(jù)庫連接,其設置由提供程序特定的 Connection 對象的 ConnectionString 屬性指定
???????????? ● Close() : 關閉數(shù)據(jù)庫連接
???????????? ● Dispose() : 法關閉或釋放由實現(xiàn)此接口的類的實例保持的文件、流和句柄等非托管資源。
???????????? ● CreateCommand(): 創(chuàng)建并返回一個與該連接相關聯(lián)的 Command 對象。
???????? 2.2.2: 相關屬性
???????????? ● 包括ConnectionString、ConnectionTimeout、Database、Sate屬性
???????????? 以上,state屬性返回的是一個ConnectionState枚舉對象,其中比較常用的的狀態(tài)值ConnectionState.Closed與ConnectionState.Open
??? 2.3 SqlConnection的一些其它特性
???????? 2.3.1 使用RetrieveStatistics()方法獲得數(shù)據(jù)命令執(zhí)行時的統(tǒng)計信息,例如,可以獲取總命令執(zhí)行時間的統(tǒng)計信息。
???????????????? 統(tǒng)計信息有以下常用屬性可以獲得:
????????????????? ●?? BytesReceived : 查詢中接收到的字節(jié)數(shù)
????????????????? ●?? BytesSend : 發(fā)送出數(shù)據(jù)的字節(jié)數(shù)
????????????????? ●?? ConnectionTime : 當前連接被開啟的總時間
????????????????? ●?? ExecutionTime : 返回以毫秒為單位的連接執(zhí)行時間
????????????????? ●?? IduCount: 用于返回被執(zhí)行Insert、Update、Delete命令的次數(shù)
????????????????? ●?? IduRows : 用于返回被執(zhí)行Insert、Update、Delete命令的行數(shù)
????????????????? ●?? SelectCount: 用于返回Select命令執(zhí)行的次數(shù)
????????????????? ●?? SelectRows : 用于返回Select命令執(zhí)行的行數(shù)
????????????????? ●?? …
例3: 取得數(shù)據(jù)庫查詢的執(zhí)行時間??????
=== App_Code\DawnDataObject.cs ===
// Movies類中的GetAll方法返回一個List對象,該對象可以被GridView等控件做為數(shù)據(jù)源綁定
namespace? DawnDataObject
{
??? public?class Movies
??? {
??????? public?static?readonly?string _connectionString;? // 連接數(shù)據(jù)庫字符串為靜態(tài)成員,每個實例共享。
??????? static Movies(){
??????????? _connectionString = WebConfigurationManager.ConnectionStrings["DawnEnterpriseDBConnectionString"].
??????????????? ConnectionString;
??????? }
??????? private?string _title;
??????? private?string _director;
??????? // Movies類中包括的屬性有Title、Director
??????? public?string Title{
??????????? get { return _title; }
??????????? set { _title = value; }
??????? }
??????? public?string Director {
??????????? get { return _director; }
??????????? set { _director = value; }
??????? }
??????? // Movies類中的GetAll方法返回一個List對象,該對象可以被GridView等控件做為數(shù)據(jù)源綁定
??????? public List<Movies> GetAll(out?long executeTime)? // executeTime作為out參數(shù)
??????? {
??????????? List<Movies> result =?new List<Movies>();
??????????? SqlConnection conn =?new SqlConnection(_connectionString);
??????????? SqlCommand comm =?new SqlCommand("WAITFOR DELAY '0:0:03';select Title,Director from Movies", conn);
??????????? conn.StatisticsEnabled =?true;?? // 開啟獲取統(tǒng)計信息的功能
??????????? using(conn){? // using關鍵字指定了conn一旦離開這個代碼段,自動調(diào)用其Dispose函數(shù)
??????????????? conn.Open();
??????????????? SqlDataReader reader = comm.ExecuteReader();
??????????????? while(reader.Read()){
??????????????????? Movies newmovie =?new Movies();
??????????????????? newmovie._title = (string)reader["Title"];
??????????????????? newmovie._director = (string)reader["Director"];
??????????????????? result.Add(newmovie);
??????????????? }
??????????????? IDictionary stats = conn.RetrieveStatistics();
??????????????? executeTime = (long)stats["ExecutionTime"];
??????????????? return result;
??????????? }
??????? }
??? }
}
=== Movies.aspx ===
<script runat=”server”>
protected void ObjectDataSource1_Selected(object sender, ObjectDataSourceStatusEventArgs e)
{
??? Label1.Text = e.OutputParameters["executeTime"].ToString();? // 取得返回參數(shù)值
}
</script>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false" DataSourceID="ObjectDataSource1">
<asp:ObjectDataSource ID="ObjectDataSource1" TypeName="DawnDataObject.Movies"?
??? SelectMethod="GetAll" runat="server" onselected="ObjectDataSource1_Selected">
??? <SelectParameters>
??????? <asp:Parameter Name="executeTime" DbType="Int64" Direction="Output"?/>?<!-- 獲得GetAll的返回參數(shù) -->
??? </SelectParameters>
</asp:ObjectDataSource>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
???????? 2.3.2 使用連接池
???????????? 數(shù)據(jù)庫連接是非常昂貴的資源,如果希望ASP.NET應用程序處理大量用戶請求的能力,使用連接池將會獲得更好的效率
???????????? 因為打開數(shù)據(jù)庫是一件很耗時的操作,每次使用數(shù)據(jù)庫時去建立連接效率將會很低,更好的辦法是創(chuàng)建一個緩存池存放被重復使用的數(shù)據(jù)庫連接。
???????????? 當使用SqlConnection時,連接時是默認開啟的,可以在連接字符串中使用Pooling=false將連接池關閉。
???????????? 關于連接池,必須注意二件事情:1.利用連接池時,調(diào)用SqlConnection.Close()方法關閉連接仍然非常重要。不顯示關閉,當前正在使用的連接就不會被放入連接池中。2.系統(tǒng)會跟據(jù)連接字符串的不同(使用字符串逐字比較方法),分別創(chuàng)建不同的連接池。因此,將連接字符串存放在站點配置文件中,盡量不要在組件代碼中硬編碼連接字符串。
???????????? ●? 清空連接池
??????????????? ClearAllPools() : 用于清空系統(tǒng)中所有連接池中的數(shù)據(jù)庫連接
??????????????? ClearPool() : 清空第統(tǒng)中指定連接池里的數(shù)據(jù)庫連接
???????????? ● 設置連接字符串中連接池的屬性 [以上這些屬性的鍵/值對是在ConnectionString中指定的]
??????????????? Connection Timeout : 用于指定為秒為單位的連接生存最大值(默認為0,永不失效)
??????????????? Connection Reset : 是否自動 重置來自連接池中的連接(默認為true)
??????????????? Max Pool Size : 保存在連接池中的最大連接數(shù)(默認為100)
??????????????? Min Pool Size : 保存在連接池中的最小連接數(shù)
??????????????? Pooling : 是否開始連接池
??? 2.4 關閉連接 Close()與Dispose()
???????? 以上兩個函數(shù)都可以關閉連接,但是它們有什么不同的。以下摘錄的就是它們不同的地方:
? Calling Dispose on a connection object alleviates the need for you to call Close on it explicitly. It not only ensures that
the underlying connection can be pooled, but it also makes sure that allocated resources can now be garbage collected.
? Not calling either Close or Dispose will effectively kill your application performance by increasing the connection pool to a maximum limit,
and then everyone will have to wait for the next available connection object. Not only that, but even when the open connections fall out of scope,
they won’t be garbage collected for a relatively long time because the connection object itself doesn’t occupy that much memory—and the lack of memory
is the sole criterion for the garbage collector to kick in and do its work.
? In short, Dispose is the best option as it helps garbage collection and connection pooling, Close is second best option as it helps only connection pooling,
and not calling either Close or Dispose is so bad that you shouldn’t even go there.
3. Command對象
??? Command對象表示一個可以對數(shù)據(jù)源執(zhí)行的命令。在這里主要介紹的是SqlCommand對象,SqlCommand繼承自Command對象的公共基類DBCommand。
??? 3.1 SQL或存儲過程 命令的執(zhí)行
?????? 可以通過SqlCommand.ExecuteNonQuery方法來執(zhí)行一個SQL命令,該方法不會返回任何一個結(jié)果集。這個方法通常用來執(zhí)行SQL語句中的Update、Insert、Delete命令。
?????? 當然也可以用來執(zhí)行如Create Table,Drop DataBase命令等
例4: 使用SqlCommand更新和刪除電影記錄的方法UpdateMovie和DeleteMovie()
=== App_Code\DawnDataObject.cs ===
namespace? DawnDataObject
{
??? public?class Movies
??? {
??????? public?static?readonly?string _connectionString;? // 連接數(shù)據(jù)庫字符串為靜態(tài)成員,每個實例共享。
??????? static Movies(){
??????????? _connectionString = WebConfigurationManager.ConnectionStrings["DawnEnterpriseDBConnectionString"].
??????????????? ConnectionString;
??????? }
??????? private?string _title;
??????? private?string _director;
??????? private Int32 _id;
??????? // Movies類中包括的屬性有Title、Director
??????? public?string Title{
??????????? get { return _title; }
??????????? set { _title = value; }
??????? }
??????? public?string Director {
??????????? get { return _director; }
??????????? set { _director = value; }
??????? }
??????? public Int32 Id {
??????????? get { return _id; }
??????? }
??????? // Movies類中的GetAll方法返回一個List對象,該對象可以被GridView等控件做為數(shù)據(jù)源綁定
??????? public List<Movies> GetAll(out?long executeTime)? // executeTime作為out參數(shù)
??????? {
??????????? List<Movies> result =?new List<Movies>();
??????????? SqlConnection conn =?new SqlConnection(_connectionString);
??????????? SqlCommand comm =?new SqlCommand("select Id,Title,Director from Movies", conn);
??????????? conn.StatisticsEnabled =?true;
??????????? using(conn){? // using關鍵字指定了conn一旦離開這個代碼段,自動調(diào)用其Dispose函數(shù)
??????????????? conn.Open();
??????????????? SqlDataReader reader = comm.ExecuteReader();
??????????????? while(reader.Read()){
??????????????????? Movies newmovie =?new Movies();
??????????????????? newmovie._title = (string)reader["Title"];
??????????????????? newmovie._director = (string)reader["Director"];
??????????????????? newmovie._id = (Int32)reader["Id"];
??????????????????? result.Add(newmovie);
??????????????? }
??????????????? IDictionary stats = conn.RetrieveStatistics();
??????????????? executeTime = (long)stats["ExecutionTime"];
??????????????? return result;
??????????? }
??????? }
??????? // 對Movies表進行更新的方法
??????? public?void UpdateMovie(int id,string title,string director)
??????? {
??????????? SqlConnection conn =?new SqlConnection(_connectionString);
??????????? SqlCommand command = conn.CreateCommand();? // 使用SqlConnection.CreateCommand獲得SqlCommand對象
??????????? command.CommandText =?"Update Movies set Title=@title,Director=@director where Id=@id";
??????????? command.Parameters.AddWithValue("@id", id);
??????????? command.Parameters.AddWithValue("@title", title);
??????????? command.Parameters.AddWithValue("@director", director);
??????????? using(conn){
??????????????? conn.Open();
??????????????? command.ExecuteNonQuery();
??????????? }
??????? }
??????? // 對Movies表進行刪除的方法
??????? public?void DeleteMovie(int id)
??????? {
??????????? SqlConnection conn =?new SqlConnection(_connectionString);
??????????? // 使用new SqlCommand獲得SqlCommand對象
??????????? SqlCommand command =?new SqlCommand("delete from Movies where Id=@id",conn);
??????????? command.Parameters.AddWithValue("@id", id);
??????????? using(conn)
??????????? {
??????????????? conn.Open();
??????????????? command.ExecuteNonQuery();
??????????? }
??????? }
??? }
}
=== Movies.aspx ===
Code<asp:GridView ID="GridView1" runat="server" DataKeyNames="Id"
??? AutoGenerateColumns="False" DataSourceID="ObjectDataSource1"
??????????? onrowcommand="GridView1_RowCommand" onrowdeleting="GridView1_RowDeleting"
??????????? AllowPaging="True"?>
<Columns>
??? <asp:BoundField HeaderText="Id" DataField="Id" Visible="false"?/>
??? <asp:BoundField HeaderText="Title" DataField="Title"?/>
??? <asp:BoundField HeaderText="Director" DataField="Director"?/>
??? <asp:CommandField ShowEditButton="True"?/>
??? <asp:CommandField ShowDeleteButton="True"?/>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" TypeName="DawnDataObject.Movies"
??? SelectMethod="GetAll" UpdateMethod="UpdateMovie" DeleteMethod="DeleteMovie" runat="server" onselected="ObjectDataSource1_Selected">
??? <SelectParameters>
??????? <asp:Parameter Name="executeTime" DbType="Int64" Direction="Output"?/>
??? </SelectParameters>
??? <UpdateParameters>
??????? <asp:Parameter Name="Id" DbType="Int32"?/>
??????? <asp:Parameter Name="Title" DbType="String"?/>
??????? <asp:Parameter Name="Director" DbType="String"?/>
??? </UpdateParameters>
??? <DeleteParameters>
??????? <asp:Parameter Name="Id" DbType="Int32"?/>
??? </DeleteParameters>
</asp:ObjectDataSource>
?
??? 3.2 執(zhí)行帶參數(shù)的命令
??????? 大多數(shù)SQL命令都帶有參數(shù),比如在執(zhí)行對數(shù)據(jù)庫記錄更新時,就要提供參數(shù)表示數(shù)據(jù)記錄項的新值
??????? 最好不要通過手工串接+=操作來構(gòu)建SQL命令參數(shù),因為這樣很容易遭受SQL注入攻擊。
??????? 而使用SqlParameter對象來表示參數(shù)有很多種構(gòu)建方式,最簡單的就像下面一樣來調(diào)用 SqlCommand.AddWithValue()方法
cmd.Parameters.AddWithValue("@title",”ASP.NET 2.0");
?????????? 當使用AddWithValue()方法時,SqlCommand自動識別并推測參數(shù)的類型和大小。該方法會假設字符串值類型為NVarChar, 整數(shù)值類型為Int,十進行數(shù)值類型為Decimal,以此類推。
?????????? 另一種構(gòu)建參數(shù)的方法是直接創(chuàng)建SqlParameter對象,并加入到SqlCommand對象中。這樣做的好處是:可以顯式指定參數(shù)名稱,大小,精度,刻度和傳遞方向。
SqlParameter paramTitle =?new SqlParameter();
paramTitle.ParameterName =?"@Title";
paramTitle.SqlDbType = SqlDbType.NVarChar;
paramTitle.Size =?50;
paramTitle.Value =?"ASP.NET";
cmd.Parameters.Add(paramTitle);
?????????? 第三種方法,直接使用Add的一個重載方法來創(chuàng)建新的SqlParameter對象
cmd.Parameters.Add("@Title",SqlDbType.NVarChar,50).Value =?"ASP.NET";
?????????? 以上,使用第一種方法比較簡便,直觀。
圖2: Command與Parameters的關系圖如下所示:
??
??? 3.3 執(zhí)行存儲過程
??????? SqlCommand對象可以用來執(zhí)行存儲過程 ,執(zhí)行存儲過程如下所示:
SqlCommand cmd = new SqlCommand("GetTitles",conn);
cmd.CommandType = CommandType.StoredProcedure;
??????? 在SqlCommand的第一個參數(shù)中,不要把參數(shù)放到里面去,只要放存儲過程名就行了,參數(shù)在SqlCommand的SqlParameters對象中添加
例5: 使用存儲過程而非SQL語句更新電影信息記錄
=== 存儲過程創(chuàng)建 ===
(
??? @id?int,
??? @title?varchar(255),
??? @director?varchar(255)
)
as
??? update movies set title=@title,director=@director?where id=@id
=== App_Code\DawnDataObject.cs ===
// 只要變更例4中的UpdateMovie函數(shù),其它代碼不變
public?void UpdateMovie(int id,string title,string director)
{
??? SqlConnection conn =?new SqlConnection(_connectionString);
??? SqlCommand command = conn.CreateCommand();? // 使用SqlConnection.CreateCommand獲得SqlCommand對象
??? // 與例4相比,只要變更下面兩句
??? command.CommandText =?"UpdateMovie";???
??? command.CommandType = CommandType.StoredProcedure;
??? command.Parameters.AddWithValue("@id", id);
??? command.Parameters.AddWithValue("@title", title);
??? command.Parameters.AddWithValue("@director", director);
??? using(conn){
??????? conn.Open();
??????? command.ExecuteNonQuery();
??? }
}
??????? 存儲過程可以有返回值(returnvalue),也可以有輸出參數(shù)(output),那么,作為程序,如何調(diào)用存儲過程后,取得相應的返回值呢。
例6: 從存儲過程中取得返回值
=== SelectMovies存儲過程 ===
AS
RETURN (SELECT?COUNT(*) FROM Movies)
=== movies.aspx ===
<script runat="server">
void Page_Load()
{
??? lblMovieCount.Text = GetMovieCount().ToString();
}
private int GetMovieCount()
{
??? int result =?0;
??? string connectionString = WebConfigurationManager.connectionString["Movies"].ConnectionString;
??? SqlConnection con =?new SqlConnection(connectionString);
??? SqlCommand cmd =?new SqlCommand("GetMovieCount", con);
??? cmd.CommandType = CommandType.StoredProcedure;
??? cmd.Parameters.Add("@ReturnVal", SqlDbType.Int).Direction =ParameterDirection.ReturnValue; // 設定返回值參數(shù)
??? using (con)
??? {
??????? con.Open();
??????? cmd.ExecuteNonQuery();? // 好像一定要使用ExecuteNonQuery,如果使用ExecuteReader,則相應的返回值就取不出來。
??????? result = (int)cmd.Parameters["@ReturnVal"].Value;? // 取得返回值參數(shù)值
??? }
??? return result;
}
</script> 例7: 從存儲過程中取得OUTPUT值
=== 存儲過程創(chuàng)建 ===
CREATE?PROCEDURE dbo.GetBoxOfficeTotals
(
??? @SumBoxOfficeTotals?Money OUTPUT
)
AS
SELECT?@SumBoxOfficeTotals?=?SUM(BoxOfficeTotals) FROM Movies
=== movies.aspx ===
<script runat=server>
public List<Movie5> GetBoxOffice(out decimal SumBoxOfficeTotals)
{
??? List<Movie5> results =?new List<Movie5>();
??? SqlConnection con =?new SqlConnection(_connectionString);
??? SqlCommand cmd =?new SqlCommand("GetBoxOfficeTotals", con);
??? cmd.CommandType = CommandType.StoredProcedure;
??? cmd.Parameters.Add("@SumBoxOfficeTotals", SqlDbType.Money).Direction = ParameterDirection.Output;
??? using (con)
??? {
??????? con.Open();
??????? SqlDataReader reader = cmd.ExecuteReader();? // 使用OUTPUT參數(shù),可以使用ExecuteReader。與ReturnValue不同。
??????? while (reader.Read())
??????? {
??????????? Movie5 newMovie =?new Movie5();
??????????? newMovie.Title = (string)reader["Title"];
??????????? newMovie.BoxOfficeTotals = (decimal)reader["BoxOfficeTotals"];
??????????? results.Add(newMovie);
??????? }
??? reader.Close();
??? SumBoxOfficeTotals = (decimal)cmd.Parameters["@SumBoxOfficeTotals"].
??? Value;
??? }
??? return results;
}
</script>
??? 3.4 單一的返回值
?????? 如果需要從數(shù)據(jù)庫查詢中獲取單一的返回值,可以使用SqlCommand.ExecuteScalar()方法。該方法總是返回查詢結(jié)果集中第一行第一列的數(shù)據(jù)值。
?????? ExecuteScalar方法返回值的類型為object,可以將它轉(zhuǎn)換為想要的類型。
??? 3.5 返回結(jié)果集
?????? 在例1、例3、例4中,利用了SqlCommand.ExecuteReader()方法,調(diào)用這個方法將返回一個SqlDataReader對象,我們使用該對象讀取每一行數(shù)據(jù),并將數(shù)據(jù)存入一個泛型集合(List<Movie>)中,
?????? 如果希望省略復制步驟,并且不把獲取的記錄存入集合對象中,那么這里需要向ExecuteReader方法傳遞一個CommandBehavior.CloseConnection參數(shù),該參數(shù)會使數(shù)據(jù)庫連接與SqlDataReader對象關聯(lián)起來,當從SqlDataReader對象中讀取了所有的數(shù)據(jù)記錄后。連接將自動關閉。
?????? 默認的向ExecuteReader方法傳遞一個CommandBehavior.Default參數(shù),它表示此查詢可能返回多個結(jié)果集。執(zhí)行查詢可能會影響數(shù)據(jù)庫狀態(tài)。調(diào)用SqlCommand.ExecuteReader(CommandBehavior.Default)就相當于調(diào)用SqlCommand.ExecuteReader()
例8: 不使用泛型集合作數(shù)據(jù)源
=== App_Code\DawnDataObject.cs ===
namespace? DawnDataObject
{
??? public?class Movies
??? {
??????? public SqlDataReader GetDataReader()
??????? {
??????????? SqlConnection conn =?new SqlConnection(_connectionString);
??????????? SqlCommand command =?new SqlCommand("SelectMovies", conn);
??????????? command.CommandType = CommandType.StoredProcedure;
??????????? conn.Open();
??????????? return command.ExecuteReader(CommandBehavior.CloseConnection);? // 直接返回DataReader作為數(shù)據(jù)源
??????? }
??????
??????? public?void UpdateMovies() {…}
??????? public?void DeleteMovies() {…}
??? }
}
=== movies.aspx ===
<!-- 因為DataReader的原因,這里的GridView不支持分頁,排序 -->
<asp:GridView ID="GridView1" runat="server" DataKeyNames="Id"
??????????? AutoGenerateColumns="False" DataSourceID="ObjectDataSource1"
??????????? onrowcommand="GridView1_RowCommand" onrowdeleting="GridView1_RowDeleting"?>
<Columns>
??? <asp:BoundField HeaderText="Id" DataField="Id" Visible="false"?/>
??? <asp:BoundField HeaderText="Title" DataField="Title"?/>
??? <asp:BoundField HeaderText="Director" DataField="Director"?/>
??? <asp:CommandField ShowEditButton="True"?/>
??? <asp:CommandField ShowDeleteButton="True"?/>
</Columns>
<asp:ObjectDataSource ID="ObjectDataSource1" TypeName="DawnDataObject.Movies"
??? SelectMethod="GetDataReader" UpdateMethod="UpdateMovie" DeleteMethod="DeleteMovie" runat="server"?>
??? <UpdateParameters>
??????? <asp:Parameter Name="Id" DbType="Int32"?/>
??????? <asp:Parameter Name="Title" DbType="String"?/>
??????? <asp:Parameter Name="Director" DbType="String"?/>
??? </UpdateParameters>
??? <DeleteParameters>
??????? <asp:Parameter Name="Id" DbType="Int32"?/>
??? </DeleteParameters>
</asp:ObjectDataSource>??
4. DataReader對象
??? DataReader對象可以用來表示數(shù)據(jù)庫查詢的結(jié)果。該對象可以通過調(diào)用Command對象的ExecuteReader()方法獲得.
??? 調(diào)用DataReader的HasRows屬性或者Read()方法,可以判斷DataReader對象所表示的查詢結(jié)果中是否包含數(shù)據(jù)行記錄
??? 調(diào)用Reader()方法,可以將DataReader對象所表示的當前數(shù)據(jù)行向前移動一行,移動一行后,返回true,到末尾,無法向前移動,返回false.
??? 任意時刻上,DataReader對象只表示查詢結(jié)果集中的某一行記錄。?
圖3:DataReader與Command關系如下圖所示:
??? 4.1 獲得DataReader對象中數(shù)據(jù)行的字段值
???????? 如果要獲得當前行中相應的字段值,可以使用以下方法來獲得:
???????? ● string title = (string)reader["title"];???? // 通過字段名稱返回Object類型,再轉(zhuǎn)換
???????? ● string title = (string)reader[0];?????????? // 通過字段位置返回Object類型,再轉(zhuǎn)換
???????? ● string title = reader.GetString(0);?????? // 通過字段位置返回string類型。
???????? ● string title = reader.GetSqlString(0);?? // 通過字段位置,返回SqlString類型。
?
???? 4.2 返回多個結(jié)果集
???????? 一個簡單的數(shù)據(jù)庫查詢可以返回多個結(jié)果集,如以下SQL語句
???????? "select * from MovieCategories; select * from movies"
???????? 在一次命令提交中執(zhí)行多個查詢可以提高效率,也可以避免戰(zhàn)勝多個數(shù)據(jù)庫連接。
???????? 在返回多個結(jié)果集的情況下,可以使用SqlDataReader的MoveResult()方法切換到下一個結(jié)果集中。
例9:返回多個結(jié)果集
=== App_Code\DawnDataObject.cs ===
namespace mulitResults
{
??? public?class DataLayer1
??? {
??????? private?static?readonly?string _connectionString;
??????? public?class MovieCategory? // 表示電影種類實體,注意是嵌套類
??????? {
??????????? private?int _id;
??????????? private?string _name;
??????????? public?int Id
??????????? {
??????????????? get { return _id; }
??????????????? set { _id = value; }
??????????? }
??????????? public?string Name
??????????? {
??????????????? get { return _name; }
??????????????? set { _name = value; }
??????????? }
??????? }
??????? public?class Movie? // 表示電影實體,注意是嵌套類
??????? {
??????????? private?string _title;
??????????? private?int _categoryId;
??????????? public?string Title
??????????? {
??????????????? get { return _title; }
??????????????? set { _title = value; }
??????????? }
??????????? public?int CategoryId
??????????? {
??????????????? get { return _categoryId; }
??????????????? set { _categoryId = value; }
??????????? }
??????? }
??????? // 不像剛才實體列表作為返回值反回,現(xiàn)在作為參數(shù)返回
??????? public?static?void GetMovieData(List<DataLayer1.MovieCategory> movieCategories,List<DataLayer1.Movie> movies)
??????? {
??????????? string commandText =?"SELECT Id,Name FROM MovieCategories;SELECT Title,CategoryId FROM Movies";
??????????? SqlConnection con =?new SqlConnection(_connectionString);
??????????? SqlCommand cmd =?new SqlCommand(commandText, con);
??????????? using (con)
??????????? {
??????????????? // Execute command
??????????????? con.Open();
??????????????? SqlDataReader reader = cmd.ExecuteReader();
??????????????? // Create movie categories
??????????????? while (reader.Read())
??????????????? {
??????????????????? DataLayer1.MovieCategory newCategory =?new DataLayer1.
??????????????????? MovieCategory();
??????????????????? newCategory.Id = (int)reader["Id"];
??????????????????? newCategory.Name = (string)reader["Name"];
??????????????????? movieCategories.Add(newCategory);
??????????????? }
??????????????? // Move to next result set
??????????????? reader.NextResult();
??????????????? // Create movies
??????????????? while (reader.Read())
??????????????? {
??????????????????? DataLayer1.Movie newMovie =?new DataLayer1.Movie();
??????????????????? newMovie.Title = (string)reader["Title"];
??????????????????? newMovie.CategoryId = (int)reader["CategoryID"];
??????????????????? movies.Add(newMovie);
??????????????? }
??????????? }
??????? }
??????? static DataLayer1()
??????? {
??????????? _connectionString = WebConfigurationManager.ConnectionStrings["Movies"].ConnectionString;
??????? }
??? }
}
=== ShowMovies.aspx ===
<script runat="server">
void Page_Load()
{
??? // Get database data
??? List<DataLayer1.MovieCategory> categories =?new List<DataLayer1.MovieCategory>();
??? List<DataLayer1.Movie> movies =?new List<DataLayer1.Movie>();
??? DataLayer1.GetMovieData(categories, movies);
??? // Bind the data
??? grdCategories.DataSource = categories;
??? grdCategories.DataBind();
??? grdMovies.DataSource = movies;
??? grdMovies.DataBind();
}
</script>
<h1>Movie Categories</h1>
<asp:GridView id="grdCategories" Runat="server"?/>
<h1>Movies</h1>
<asp:GridView id="grdMovies" Runat="server"?/>
??? 4.3 多活動結(jié)果集MARS (Multiple Active Resultsets)
??????? ADO.NET 2.0提供了MARS的新特性,在以前版本的ADO.NET中,數(shù)據(jù)庫連接在一個有限時間段內(nèi)能且只能表示一個查詢結(jié)果集。
??????? 利用MARS特性,可以使用單一的數(shù)據(jù)庫連接表示多個查詢結(jié)果集。
??????? MARS在以下場景中很有價值:程序?qū)σ粋€結(jié)果集遍歷的過程中,同時需要對當前結(jié)果集中的記錄執(zhí)行一些額外的數(shù)據(jù)庫操作。
??????? 打開MARS功能,需要在連接字符口串中包含以下字段: MultipleActiveResultSets=True;
例10: 使用多個結(jié)果集
<script runat=”server”>
void BuildTree()
{
??? // Create MARS connection
??? SqlConnection con =?new SqlConnection(_connectionString);
??? // Create Movie Categories command
??? string cmdCategoriesText =?"SELECT Id,Name FROM MovieCategories";
??? SqlCommand cmdCategories =?new SqlCommand(cmdCategoriesText, con);
??? // Create Movie command
??? string cmdMoviesText =?"SELECT Title FROM Movies "?+?"WHERE CategoryId=@CategoryID";
??? SqlCommand cmdMovies =?new SqlCommand(cmdMoviesText, con);
??? cmdMovies.Parameters.Add("@CategoryId", SqlDbType.Int);
??? using (con)
??? {
??????? con.Open();
??????? // 打開一個結(jié)果集,表示電影目錄
??????? SqlDataReader categories = cmdCategories.ExecuteReader();
??????? while (categories.Read())
??????? {
??????????? // Add category node
??????????? int id = categories.GetInt32(0);
??????????? string name = categories.GetString(1);
??????????? TreeNode catNode =?new TreeNode(name);
??????????? TreeView1.Nodes.Add(catNode);
??????????? // Iterate through matching movies
??????????? cmdMovies.Parameters["@CategoryId"].Value = id;
??????????? SqlDataReader movies = cmdMovies.ExecuteReader();? // 打開另一個結(jié)果集.注:上一個結(jié)果集還沒有被關閉.
??????????? while (movies.Read())
??????????? {
??????????????? // Add movie node
??????????????? string title = movies.GetString(0);
??????????????? TreeNode movieNode =?new TreeNode(title);
??????????????? catNode.ChildNodes.Add(movieNode);
??????????? }
??????????? movies.Close();
??? }
}
void Page_Load()
{
??? if (!Page.IsPostBack)
??????? BuildTree();
}
</script>
<asp:TreeView id=”TreeView1” Runat=”server” />
總結(jié)
以上是生活随笔為你收集整理的asp.net学习之ado.net(连接模式访问)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么让plsqldev恢复界面视图复位
- 下一篇: .Net程序员学用Oracle系列(18