在ASP.NET 2.0中操作數(shù)據(jù)之七十:配置數(shù)據(jù)庫(kù)連接和命令等級(jí)設(shè)置
導(dǎo)言:
在本系列我們用類型化的DataSets來(lái)構(gòu)建數(shù)據(jù)訪問(wèn)層。就像在第一章探討的那樣,類型化DataSets的DataTables用作存儲(chǔ)數(shù)據(jù)的“倉(cāng)庫(kù)”,而TableAdapters作為連接數(shù)據(jù)庫(kù)的通道,以檢索、修改數(shù)據(jù).TableAdapters 將處理數(shù)據(jù)庫(kù)的很多復(fù)雜的細(xì)節(jié)進(jìn)行了封裝,將我們解脫出來(lái),免去了寫代碼連接數(shù)據(jù)庫(kù)、發(fā)出命名、向DataTable填充數(shù)據(jù)的痛苦.
不過(guò)在某些時(shí)候我們需要深入的探究TableAdapter,直接寫代碼處理ADO.NET對(duì)象.在第61章《在事務(wù)里對(duì)數(shù)據(jù)庫(kù)修改進(jìn)行封裝》里我們向TableAdapter添加了多個(gè)方法以開啟、提交、回滾ADO.NET事務(wù).這些方法都使用內(nèi)在的、手動(dòng)創(chuàng)建的SqlTransaction對(duì)象來(lái)對(duì)TableAdapter的SqlCommand對(duì)象進(jìn)行賦值.
在本文,我們將考察如何訪問(wèn)TableAdapter的“數(shù)據(jù)庫(kù)連接”和“數(shù)據(jù)庫(kù)命令”級(jí)的設(shè)置.具體來(lái)說(shuō),我們將向ProductsTableAdapter添加函數(shù),以訪問(wèn)“連接字符串”(connection string)和“命令過(guò)期時(shí)間”(command timeout)設(shè)置.
用ADO.NET處理數(shù)據(jù)
微軟.NET Framework包含了很多處理數(shù)據(jù)的特殊用途的類。這些類用System.Data namespace來(lái)進(jìn)行創(chuàng)建,其中就包括ADO.NET classe類,一些ADO.NET名下的類需要依賴某個(gè)特定的data provider才能工作.你可以想象在ADO.NET classes類和某個(gè)數(shù)據(jù)存儲(chǔ)(data store)之間,有一個(gè)data provider充當(dāng)連接通道(communication channel)以供傳遞信息.data provider包括OleDb 、ODBC, 以及其它一些專門設(shè)計(jì)來(lái)連接某種特定數(shù)據(jù)庫(kù)系統(tǒng)的data provider.舉個(gè)例子,我們不能用OleDb來(lái)連接一個(gè)Microsoft SQL Server數(shù)據(jù)庫(kù).而SqlClient就可以,因?yàn)樗墙?jīng)過(guò)優(yōu)化的專門設(shè)計(jì)來(lái)連接SQL Server的.
當(dāng)編程訪問(wèn)數(shù)據(jù)時(shí),通常使用下面的模式:
1.新建數(shù)據(jù)庫(kù)連接
2.發(fā)出命令
3.用SELECT查詢來(lái)返回記錄
以上3步每步都有單獨(dú)的ADO.NET classes類來(lái)執(zhí)行.比如連接數(shù)據(jù)庫(kù)用SqlConnection class類;要發(fā)出INSERT, UPDATE, DELETE,或SELECT命令,用SqlCommand class類.
除了第61章《在事務(wù)里對(duì)數(shù)據(jù)庫(kù)修改進(jìn)行封裝》外,我們都沒(méi)有自己寫任何ADO.NET代碼,因?yàn)門ableAdapters自動(dòng)生成的代碼包含了一些必要的功能:連接數(shù)據(jù)庫(kù)、發(fā)出命令、檢索數(shù)據(jù)、填充DataTables.但是有時(shí)我們要自己定制這些設(shè)置.在接下來(lái)的幾步我們將探究TableAdapters內(nèi)部使用的ADO.NET對(duì)象.
第一步:考察Connection屬性
每個(gè)TableAdapter class類都有一個(gè)Connection屬性,用于指定數(shù)據(jù)庫(kù)連接信息.該屬性的數(shù)據(jù)類型以及ConnectionString的值根據(jù)TableAdapter設(shè)置向?qū)龅呐渲枚?我們還記得,當(dāng)向類型化的DataSet添加一個(gè)TableAdapter時(shí),向?qū)б覀冎付〝?shù)據(jù)源(見(jiàn)圖1).在下拉列表里列出了web.config文件指定連接的數(shù)據(jù)庫(kù),以及服務(wù)器資源管理器的Data Connections里的數(shù)據(jù)庫(kù).如果我們要連接的數(shù)據(jù)庫(kù)沒(méi)有出現(xiàn)在下拉列表里,點(diǎn)“New Connection”按鈕,以提供必需的連接信息.
圖1:TableAdapter設(shè)置向?qū)У牡谝徊?/strong>
我們化點(diǎn)時(shí)間來(lái)查看TableAdapter的Connection屬性的代碼,就像在第一章《創(chuàng)建一個(gè)數(shù)據(jù)訪問(wèn)層》里探討的一樣,我們可以在Class View窗口里查看自動(dòng)生成的TableAdapter代碼,轉(zhuǎn)到相應(yīng)的類,再雙擊成員名(member name)即可.
打開“View”菜單,選中“Class View”(或按住Ctrl+Shift+C).在Class View窗口的上半部分里,選中NorthwindTableAdapters命名空間,再選中ProductsTableAdapter class類.這將在下半部分顯示出ProductsTableAdapter的成員,如圖2所示.雙擊Connection屬性以查看代碼。
圖2:雙擊Connection以查看自動(dòng)生成的代碼
TableAdapter的Connection屬性以及其它與連接相關(guān)的代碼如下:
private System.Data.SqlClient.SqlConnection _connection; private void InitConnection() { this._connection = new System.Data.SqlClient.SqlConnection(); this._connection.ConnectionString = ConfigurationManager.ConnectionStrings["NORTHWNDConnectionString"].ConnectionString; } internal System.Data.SqlClient.SqlConnection Connection { get { if ((this._connection == null)) { this.InitConnection(); } return this._connection; } set { this._connection = value; if ((this.Adapter.InsertCommand != null)) { this.Adapter.InsertCommand.Connection = value; } if ((this.Adapter.DeleteCommand != null)) { this.Adapter.DeleteCommand.Connection = value; } if ((this.Adapter.UpdateCommand != null)) { this.Adapter.UpdateCommand.Connection = value; } for (int i = 0; (i < this.CommandCollection.Length); i = (i + 1)) { if ((this.CommandCollection[i] != null)) { ((System.Data.SqlClient.SqlCommand) (this.CommandCollection[i])).Connection = value; } } } }
當(dāng)對(duì)TableAdapter class類進(jìn)行“實(shí)例化”(instantiated)時(shí),成員變量_connection的值等同為null.當(dāng)訪問(wèn)Connection屬性時(shí),首先檢查是否已經(jīng)對(duì)成員變量_connection實(shí)例化,如果沒(méi)有的話就調(diào)用InitConnection方法,該方法對(duì)_connection進(jìn)行實(shí)例化,然后用TableAdapter設(shè)置向?qū)е付ǖ倪B接字符串對(duì)其賦值.
同樣可以用Connection屬性對(duì)一個(gè)SqlConnection對(duì)象賦值,這樣的話就可以將這個(gè)新的SqlConnection對(duì)象與TableAdapter的SqlCommand對(duì)象聯(lián)系起來(lái).
第二步:訪問(wèn)“數(shù)據(jù)庫(kù)連接”級(jí)的設(shè)置
數(shù)據(jù)庫(kù)連接信息封裝在TableAdapter內(nèi)部,很難從應(yīng)用程序的其它層對(duì)其進(jìn)行訪問(wèn).然而,在某些時(shí)候,某人查詢、用戶或ASP.NET頁(yè)面需要對(duì)TableAdapter的連接信息進(jìn)行訪問(wèn)或定制.
讓我們對(duì)Northwind數(shù)據(jù)集的ProductsTableAdapter進(jìn)行擴(kuò)展,以包含一個(gè)ConnectionString屬性,以便于在業(yè)務(wù)邏輯層對(duì)TableAdapter用到的連接字符串進(jìn)行讀取和更改.
注意:一個(gè)連接字符串是這樣的一個(gè)字符串,它指定了數(shù)據(jù)庫(kù)連接信息.比如,提供者provider、數(shù)據(jù)庫(kù)的位置、身份驗(yàn)證,以及其它與數(shù)據(jù)庫(kù)相關(guān)的設(shè)置.更多詳情請(qǐng)參考網(wǎng)站ConnectionStrings.com
就像在第一章《創(chuàng)建一個(gè)數(shù)據(jù)訪問(wèn)層》里討論過(guò)的一樣,類型化的DataSet自動(dòng)生成的類可以通過(guò)使用部分類(partial classes)來(lái)進(jìn)行擴(kuò)充.首先,在~/App_Code/DAL文件夾里新建一個(gè)名為ConnectionAndCommandSettings的文件夾.
圖3:添加一個(gè)名為ConnectionAndCommandSettings的文件夾
在里面添加一個(gè)ProductsTableAdapter.ConnectionAndCommandSettings.cs文件,鍵入如下的代碼:
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; namespace NorthwindTableAdapters { public partial class ProductsTableAdapter { public string ConnectionString { get { return this.Connection.ConnectionString; } set { this.Connection.ConnectionString = value; } } } }
該局部類為ProductsTableAdapter class類添加了一個(gè)public類型的,名為ConnectionString的屬性.該屬性允許在任何層對(duì)TableAdapter用到的連接字符串進(jìn)行讀取和更改.
當(dāng)創(chuàng)建并保存該局部類后,打開ProductsBLL class類。打開其中的一個(gè)方法,鍵入Adapter,再輸入其范圍內(nèi)的一個(gè)關(guān)鍵字以打開智能感知,你應(yīng)該可以看到這個(gè)新添加的的ConnectionString屬性出現(xiàn)在智能感知里,這就表明了我們可以在BLL層通過(guò)編程來(lái)讀取或更改其值.
訪問(wèn)整個(gè)Connection對(duì)象
該局部類擴(kuò)展的只是connection對(duì)象眾多屬性中的一個(gè):ConnectionString.如果你想在TableAdapter范圍外訪問(wèn)整個(gè)connection對(duì)象的話,你可以改變Connection屬性的保護(hù)等級(jí).就像我們?cè)诘谝徊娇疾斓哪菢?,TableAdapter的Connection屬性標(biāo)明為internal,這就是說(shuō),只有在同級(jí)的類里才可以對(duì)其進(jìn)行訪問(wèn).不過(guò)我們可以通過(guò)TableAdapter的 ConnectionModifier屬性來(lái)改變其訪問(wèn)范圍.
打開Northwind數(shù)據(jù)集,在ProductsTableAdatper上右鍵單擊,打開屬性窗口,你將會(huì)看到ConnectionModifier設(shè)置為默認(rèn)的Assembly. 為了在數(shù)據(jù)集范圍以外訪問(wèn)Connection屬性,我們將其改為Public.
圖4:可以通過(guò)ConnectionModifier屬性修改Connection屬性的訪問(wèn)范圍
保存后在返回到ProductsBLL class類,就向前面一樣在某個(gè)現(xiàn)有的方法內(nèi)鍵入Adapter,再輸入其范圍內(nèi)的一個(gè)關(guān)鍵字以打開智能感知,你應(yīng)該也可看到一個(gè)Connection屬性,這意味著我們可以在業(yè)務(wù)邏輯層通過(guò)編程對(duì)連接設(shè)置進(jìn)行讀取或賦值操作.
第三步:考察與Command相關(guān)的屬性
一個(gè)TableAdapter有一個(gè)主查詢(main query),主查詢默認(rèn)情況下會(huì)自動(dòng)生成INSERT, UPDATE,以及DELETE statements.該主查詢的INSERT, UPDATE,DELETE statements在TableAdapter的代碼里通過(guò)Adapter屬性以一個(gè)ADO.NET data adapter object(ADO.NET數(shù)據(jù)適配器對(duì)象)的形式來(lái)執(zhí)行.
由于我們的教程使用的SqlClient provider,因此Adapter屬性為SqlDataAdapter類型.
TableAdapter的Adapter屬性有3個(gè)SqlCommand類型的屬性,分別用來(lái)發(fā)出INSERT, UPDATE,DELETE statements:
.InsertCommand
.UpdateCommand
.DeleteCommand
一個(gè)SqlCommand對(duì)象負(fù)責(zé)向數(shù)據(jù)庫(kù)發(fā)出某個(gè)具體的查詢,并有相應(yīng)的屬性,比如:CommandText屬性包含了要執(zhí)行的ad-hoc SQL statement或存儲(chǔ)過(guò)程;Parameters屬性是一個(gè)SqlParameter對(duì)象集。就像我們?cè)诘谝徽隆?創(chuàng)建一個(gè)數(shù)據(jù)訪問(wèn)層》探討的一樣,這些command對(duì)象可以通過(guò)屬性窗口進(jìn)行用戶定制.
除了主查詢外,TableAdapter還包含一系列的方法,當(dāng)調(diào)用這些方法時(shí),就向數(shù)據(jù)庫(kù)發(fā)出具體的命令.主查詢的command對(duì)象以及其它所有方法使用的command對(duì)象都保存在TableAdapter的CommandCollection屬性里.
我們花點(diǎn)時(shí)間看看Northwind數(shù)據(jù)集里的ProductsTableAdapter生成的有關(guān)這2個(gè)屬性及其支持的成員變量、方法:
private System.Data.SqlClient.SqlDataAdapter _adapter; private void InitAdapter() { this._adapter = new System.Data.SqlClient.SqlDataAdapter(); ... Code that creates the InsertCommand, UpdateCommand, ... ... and DeleteCommand instances - omitted for brevity ... } private System.Data.SqlClient.SqlDataAdapter Adapter { get { if ((this._adapter == null)) { this.InitAdapter(); } return this._adapter; } } private System.Data.SqlClient.SqlCommand[] _commandCollection; private void InitCommandCollection() { this._commandCollection = new System.Data.SqlClient.SqlCommand[9]; ... Code that creates the command objects for the main query and the ... ... ProductsTableAdapters other eight methods - omitted for brevity ... } protected System.Data.SqlClient.SqlCommand[] CommandCollection { get { if ((this._commandCollection == null)) { this.InitCommandCollection(); } return this._commandCollection; } }
Adapter 和 CommandCollection屬性的代碼與Connection屬性的代碼很相似.這些屬性的get模塊一開始檢測(cè)相應(yīng)的成員變量是否為null,如果是,那么調(diào)用一個(gè)方法,以創(chuàng)建這個(gè)成員變量的一個(gè)實(shí)例,然后對(duì)與command相關(guān)的屬性進(jìn)行賦值.
第四步:訪問(wèn)與Command相關(guān)的設(shè)置
理想狀態(tài)下,命令級(jí)(command-level)的信息應(yīng)該封裝在數(shù)據(jù)訪問(wèn)層。當(dāng)然,我們也可以通過(guò)一個(gè)部分類來(lái)對(duì)其進(jìn)行擴(kuò)展,就像數(shù)據(jù)庫(kù)連接級(jí)(connection-level)的設(shè)置一樣.
由于TableAdapter只有一個(gè)單一的Connection屬性,所以那些用于擴(kuò)展數(shù)據(jù)庫(kù)連接級(jí)設(shè)置的代碼是非常直觀易懂的.而如果要修改命令級(jí)的設(shè)置的話要復(fù)雜一些,這是因?yàn)門ableAdapter包含了多個(gè)command對(duì)象——InsertCommand, UpdateCommand,DeleteCommand, 以及CommandCollection 屬性包含的數(shù)量不等的command對(duì)象.當(dāng)更新命令級(jí)的設(shè)置時(shí),這些設(shè)置需要告知所有的這些command對(duì)象.
比如,假如在一個(gè)TableAdapter里某些查詢需要花很長(zhǎng)的時(shí)間才能執(zhí)行.當(dāng)使用該TableAdapter來(lái)執(zhí)行其中的一個(gè)查詢時(shí),我們可能希望增大command對(duì)象的CommandTimeout屬性的值.該屬性指定了等待命令執(zhí)行的時(shí)間,默認(rèn)為30秒.
想要從業(yè)務(wù)邏輯層來(lái)調(diào)整CommandTimeout屬性的話,可以用我們?cè)诘诙嚼飫?chuàng)建的部分類向ProductsDataTable添加一個(gè)public類型的方法,如下:(在ProductsTableAdapter.ConnectionAndCommandSettings.cs文件添加):
public void SetCommandTimeout(int timeout) { if (this.Adapter.InsertCommand != null) this.Adapter.InsertCommand.CommandTimeout = timeout; if (this.Adapter.DeleteCommand != null) this.Adapter.DeleteCommand.CommandTimeout = timeout; if (this.Adapter.UpdateCommand != null) this.Adapter.UpdateCommand.CommandTimeout = timeout; for (int i = 0; i < this.CommandCollection.Length; i++) if (this.CommandCollection[i] != null) this.CommandCollection[i].CommandTimeout = timeout; }
我們可以從BLL層或表現(xiàn)層調(diào)用該方法,以設(shè)置某個(gè)TableAdapter實(shí)例發(fā)出的所有命令的command timeout值.
注意:Adapter 和 CommandCollection屬性被標(biāo)記為private,這就意味著只能在TableAdapter內(nèi)部對(duì)其訪問(wèn).于Connection屬性不同,我們不能改變其訪問(wèn)權(quán)限配置.因此,如果你想對(duì)其進(jìn)行擴(kuò)展以便從體系的其它層對(duì)其進(jìn)行訪問(wèn)的話,像上面討論的那樣,我們必須使用部分類來(lái)提供一個(gè)public類型的方法或?qū)傩裕瑢?duì)這些標(biāo)記為private的命令對(duì)象進(jìn)行讀寫操作.
結(jié)語(yǔ):
TableAdapters將數(shù)據(jù)訪問(wèn)等細(xì)節(jié)進(jìn)行的封裝,因此我們使用TableAdapters的時(shí)候,不用關(guān)心手寫ADO.NET代碼以連接數(shù)據(jù)庫(kù)、發(fā)出命令、用檢索的數(shù)據(jù)填充DataTable等.因?yàn)樗呀?jīng)自動(dòng)生成了這些代碼.
但是在某些時(shí)候,我們需要定制這些ADO.NET細(xì)節(jié),比如改變連接字符串或默認(rèn)的command timeout和connection timeout的值.TableAdapter自動(dòng)生成了Connection, Adapter,以及CommandCollection屬性,但是默認(rèn)情況下這些屬性要么標(biāo)記為internal要么為private.我們可以對(duì)這些內(nèi)部信息進(jìn)行擴(kuò)展,方法就是使用部分類,在部分類里使用標(biāo)記為public的方法或?qū)傩?另外,對(duì)TableAdapter的Connection屬性,我們可以通過(guò)TableAdapter的ConnectionModifier屬性來(lái)改變其訪問(wèn)權(quán)限.
祝編程快樂(lè)!
作者簡(jiǎn)介
本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創(chuàng)始人,自1998年以來(lái)一直應(yīng)用 微軟Web技術(shù)。大家可以點(diǎn)擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數(shù)據(jù)教程》,希望對(duì)大家的學(xué)習(xí)ASP.NET有所幫助。
- 調(diào)試ASP.NET應(yīng)用程序的方法和技巧
- asp.net程序編譯調(diào)試時(shí)偶爾出現(xiàn)訪問(wèn)被拒絕的錯(cuò)誤的解決方法
- C# 動(dòng)態(tài)編譯、動(dòng)態(tài)執(zhí)行、動(dòng)態(tài)調(diào)試
- ASP.NET筆記之頁(yè)面跳轉(zhuǎn)、調(diào)試、form表單、viewstate、cookie的使用說(shuō)明
- C#中一些你可能沒(méi)用過(guò)的調(diào)試窗口的方法
- Asp.net調(diào)試的一些問(wèn)題小結(jié)
- 調(diào)試ASP.NET2005/2008時(shí),端口不正確的解決三套方案
- 在ASP.NET 2.0中操作數(shù)據(jù)之六十八:為DataTable添加額外的列
- 在ASP.NET 2.0中操作數(shù)據(jù)之六十九:處理Computed Columns列
- 在ASP.NET 2.0中操作數(shù)據(jù)之七十一:保護(hù)連接字符串及其它設(shè)置信息
- 在ASP.NET 2.0中操作數(shù)據(jù)之七十二:調(diào)試存儲(chǔ)過(guò)程
相關(guān)文章
在ASP.NET 2.0中操作數(shù)據(jù)之四十二:DataList和Repeater數(shù)據(jù)排序(一)
本文主要介紹利用ObjectDataSource的Selecting事件進(jìn)行DataList和Repeater數(shù)據(jù)排序的方法,DropDownList隱式的為我們將sort expression 和 direction保存在它的view state里,進(jìn)行分頁(yè)時(shí)從view state中取出條件進(jìn)行排序。2016-05-05解讀ASP.NET 5 & MVC6系列教程(15):MvcOptions配置
這篇文章主要介紹了ASP.NET 5 MVC6中MvcOptions配置方法,需要的朋友可以參考下2016-06-06向asp.Net進(jìn)發(fā) 數(shù)據(jù)庫(kù)連接操作
一直都在考慮選擇新的WEB開發(fā)語(yǔ)言,可是在JSP/PHP/.Net三者之間來(lái)回徘徊了許久,還是不知道該往那個(gè)方向走!2008-10-10NopCommerce架構(gòu)分析之(三)EntityFramework數(shù)據(jù)庫(kù)初試化及數(shù)據(jù)操作
本文介紹IStartupTask,該類會(huì)在系統(tǒng)啟動(dòng)時(shí)執(zhí)行,IStartupTask調(diào)用IEfDataProvider進(jìn)行數(shù)據(jù)庫(kù)的初始化。2016-04-04在ASP.NET 2.0中操作數(shù)據(jù)之六十三:GridView實(shí)現(xiàn)批量刪除數(shù)據(jù)
本文主要介紹在GridView控件中包含一個(gè)checkbox列來(lái)實(shí)現(xiàn)復(fù)選多條數(shù)據(jù),在批量刪除按鈕的事件中通過(guò)for循環(huán)來(lái)一一刪除。2016-05-05《解剖PetShop》之一:PetShop的系統(tǒng)架構(gòu)設(shè)計(jì)
PetShop是一個(gè)范例,微軟用它來(lái)展示.Net企業(yè)系統(tǒng)開發(fā)的能力。本文主要講解PetShop4.0的系統(tǒng)架構(gòu)設(shè)計(jì),需要的朋友可以參考下。2016-05-05在ASP.NET 2.0中操作數(shù)據(jù)之四十六:使用SqlDataSource控件檢索數(shù)據(jù)
在前面的教程里,我們用ObjectDataSource控件充分的將表現(xiàn)層和數(shù)據(jù)訪問(wèn)層(DAL)分開來(lái)。在這篇教程里我們看看怎樣在一個(gè)表現(xiàn)層和數(shù)據(jù)訪問(wèn)層區(qū)分的不是很嚴(yán)格的簡(jiǎn)單程序中使用SqlDataSource控件。2016-05-05在ASP.NET 2.0中操作數(shù)據(jù)之十:使用 GridView和DetailView實(shí)現(xiàn)的主/從報(bào)表
本文我們主要研究了如何使用可選擇行的GridView顯示主記錄,以及在DetailsView中顯示選中記錄的詳細(xì)信息。2016-05-05在ASP.NET 2.0中操作數(shù)據(jù)之三十一:使用DataList來(lái)一行顯示多條記錄
ASP.NET 2.0中DataList默認(rèn)情況使用單列多行的table來(lái)顯示項(xiàng),本文介紹通過(guò)設(shè)置RepeatColumns屬性為每行的列數(shù)就可以達(dá)到顯示多條記錄這個(gè)目的。2016-05-05Microsoft .Net Remoting系列教程之一:.Net Remoting基礎(chǔ)篇
本文主要講解.Net Remoting的基礎(chǔ),需要的朋友可以參考下。2016-05-05