国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

.NET 數(shù)據(jù)訪問(wèn)架構(gòu)指南

2019-11-17 04:37:16
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
概述:本文提供了在多層.NET應(yīng)用程序中實(shí)施基于ADO.NET的數(shù)據(jù)訪問(wèn)層的指導(dǎo)原則。其重點(diǎn)是一組通用數(shù)據(jù)訪問(wèn)任務(wù)和方案,并指導(dǎo)你選擇最合適的途徑和技術(shù)(68張打印頁(yè))。

  簡(jiǎn)介

  假如你在為.NET應(yīng)用程序設(shè)計(jì)數(shù)據(jù)訪問(wèn)層,那么就應(yīng)該把 Microsoft ADO.NET用作數(shù)據(jù)訪問(wèn)模型。ADO.NET擴(kuò)展豐富,并且支持結(jié)合松散的數(shù)據(jù)訪問(wèn)需求、多層Web應(yīng)用程序及Web服務(wù)。通常,它利用許多擴(kuò)展豐富的對(duì)象模型, ADO.NET提供了多種方法用于解決一個(gè)特定問(wèn)題。

  本文將指導(dǎo)你選擇最合適的數(shù)據(jù)訪問(wèn)方法,其做法是具體列出大范圍的通用數(shù)據(jù)訪問(wèn)方案,提供運(yùn)用技巧,并且建議最優(yōu)實(shí)踐。本文還回答了其它經(jīng)常問(wèn)到的問(wèn)題:何處最適合存放數(shù)據(jù)庫(kù)鏈接字符串?應(yīng)如何實(shí)現(xiàn)鏈接存儲(chǔ)池?如何處理事務(wù)?如何實(shí)現(xiàn)分頁(yè)以答應(yīng)用戶在許多記錄中滾動(dòng)?

  注重本文的重點(diǎn)是ADO.NET的使用:利用SQL Server .NETData PRovider--隨ADO.NET一起提供的兩個(gè)供給器之一--訪問(wèn)Microsoft SQL Server 2000。本文在合適的地方,將突出顯示在你使用OLE DB .NET數(shù)據(jù)供給器訪問(wèn)其它OLE DB敏感數(shù)據(jù)源時(shí)需要注重的所有差別。

  對(duì)于利用本文所討論的指導(dǎo)原則和最優(yōu)實(shí)踐所開(kāi)發(fā)的數(shù)據(jù)訪問(wèn)組件的具體實(shí)現(xiàn),見(jiàn)(Data access application Block)數(shù)據(jù)訪問(wèn)應(yīng)用程序塊。注重,本實(shí)現(xiàn)的源代碼是可以獲得的,并且能直接用于你的.NET應(yīng)用程序中。

  誰(shuí)應(yīng)當(dāng)閱讀本文?

  本文為希望構(gòu)建.NET應(yīng)用程序的應(yīng)用程序設(shè)計(jì)師和企業(yè)開(kāi)發(fā)人員提供了指導(dǎo)原則。假如你負(fù)責(zé)設(shè)計(jì)并開(kāi)發(fā)多層.NET應(yīng)用程序的數(shù)據(jù)層,那么請(qǐng)閱讀本文。

  你首先需要知道什么?

  要利用本指南構(gòu)建.NET應(yīng)用程序,你必須有利用ActiveX數(shù)據(jù)對(duì)象(ADO)和/或 OLE DB開(kāi)發(fā)數(shù)據(jù)訪問(wèn)代碼的實(shí)際經(jīng)驗(yàn),及SQL Server經(jīng)驗(yàn)。你也必須明白如何為.NET平臺(tái)開(kāi)發(fā)治理代碼,并且也必須清楚ADO.NET數(shù)據(jù)訪問(wèn)模型引入的基本變化。

  ADO.NET簡(jiǎn)介

  ADO.NET是.NET應(yīng)用程序的數(shù)據(jù)訪問(wèn)模型。它能用于訪問(wèn)關(guān)系型數(shù)據(jù)庫(kù)系統(tǒng),如SQL Server 2000,及很多其它已經(jīng)配備了OLE DB供給器的數(shù)據(jù)源。在某種程度上,ADO.NET代表了最新版本的ADO技術(shù)。然而,ADO.NET引入了一些重大變化和革新,它們專門用于結(jié)構(gòu)松散的、本質(zhì)非鏈接的Web應(yīng)用程序。關(guān)于ADO 與 ADO.NET的比較,見(jiàn)MSDN中的“用于ADO程序員的ADO.NET”一文。

  ADO.NET引入的一個(gè)重要變化是,用DataTable, DataSet, DataAdapter, 和 DataReader對(duì)象的組合代替了ADO Recordset對(duì)象。DataTable表示來(lái)自一個(gè)表的行集合,在這方面它與Recordset類似。DataSet表示DataTable對(duì)象的集合,及與其它表綁定在一起的關(guān)系和限制。實(shí)際上,DataSet是具有內(nèi)置的擴(kuò)展標(biāo)記語(yǔ)言(xml)支持的內(nèi)存中的關(guān)聯(lián)結(jié)構(gòu)。

  DataSet的一個(gè)主要特點(diǎn)是,它對(duì)底層的數(shù)據(jù)源一無(wú)所知,而這些數(shù)據(jù)源可能用于對(duì)其進(jìn)行填充。這是一個(gè)分離的用于表示數(shù)據(jù)集合的獨(dú)立實(shí)體,并且它可通過(guò)多層應(yīng)用程序的不同層由一個(gè)組件傳遞到另一組件。它也可作為XML 數(shù)據(jù)流被序列化,因而非常適合于不同類型平臺(tái)間的數(shù)據(jù)傳輸。ADO.NET使用DataAdapter對(duì)象為發(fā)送到和來(lái)自DataSet及底層數(shù)據(jù)源的數(shù)據(jù)建立通道。DataAdapter對(duì)象還支持增強(qiáng)的批更新特性,以前這是Recorder的相關(guān)功能。

  .NET 數(shù)據(jù)供給器

  ADO.NET 依靠.NET 數(shù)據(jù)供給器的服務(wù)。 它們提供了對(duì)底層數(shù)據(jù)源的訪問(wèn),包括四個(gè)主要對(duì)象(Connection, Command, DataReader,及DataAdapter),目前,ADO.NET只發(fā)行了兩個(gè)供給器:
  • SQL Server .NET 數(shù)據(jù)供給器。這是用于Microsoft SQL Server 7.0及其以后版本數(shù)據(jù)庫(kù)的供給器,它優(yōu)化了對(duì)SQL Server的訪問(wèn),并利用 SQL Server內(nèi)置的數(shù)據(jù)轉(zhuǎn)換協(xié)議直接與SQL Server通信。

  • 當(dāng)鏈接到SQL Server 7.0 或 SQL Server 2000時(shí),總是要使用此供給器。

  • OLE DB .NET 數(shù)據(jù)供給器。. 這是一個(gè)用于治理OLE DB 數(shù)據(jù)源的供給器。它的效率稍低于SQL Server .NET Data Provider,因?yàn)樵谂c數(shù)據(jù)庫(kù)通信時(shí),它需通過(guò)OLE DB層進(jìn)行呼叫。注重,此供給器不支持用于開(kāi)放數(shù)據(jù)庫(kù)鏈接(ODBC),MSDASQL的OLE DB供給器。對(duì)于ODBC數(shù)據(jù)源,應(yīng)使用ODBC .NET數(shù)據(jù)供給器。有關(guān)與ADO.NET兼容的OLE DB供給器列表。
  目前測(cè)試版中的其它.NET數(shù)據(jù)供給器包括:
  • ODBC .NET 數(shù)據(jù)供給器。目前Beta 1.0版可供下載。它提供了對(duì)ODBC驅(qū)動(dòng)器的內(nèi)置訪問(wèn),其方式與OLE DB .NET數(shù)據(jù)供給器提供的對(duì)本地OLE DB供給器的訪問(wèn)方式相同。關(guān)于ODBC .NET及Beta版下載的更多信息見(jiàn).

  • 用于從SQL Server 2000中得到XML的治理供給器。用于SQL Server Web升級(jí)2版的XML還包括了專用于從SQL Server 2000中得到XML的治理供給器。關(guān)于此升級(jí)版本的更多信息,見(jiàn) .
  名稱空間組織

  與每個(gè).NET數(shù)據(jù)供給器相關(guān)的類型(類,結(jié)構(gòu),枚舉,等等)位于它們各自的名稱空間中:
  • System.Data.SqlClient. 包含了 SQL Server .NET 數(shù)據(jù)供給器類型。


  • System.Data.OleDb. 包含了 OLE DB .NET數(shù)據(jù)供給器類型。

  • System.Data.Odbc. 包含了ODBC .NET數(shù)據(jù)供給器類型。

  • System.Data. 包含了獨(dú)立于供給器的類型,如DataSet及DataTable。
  在各自關(guān)聯(lián)的名稱空間中,每個(gè)供給器都提供了Connection, Command, DataReader, 及 DataAdapter對(duì)象的實(shí)現(xiàn)。SqlClient實(shí)現(xiàn)都有前綴"Sql";而OleDb實(shí)現(xiàn)前面都有前綴"OleDb"。例如,Connection對(duì)象的 SqlClient實(shí)現(xiàn)是SqlConnection。而OleDb實(shí)現(xiàn)是OleDbConnection。類似的,DataAdapter對(duì)象的兩種實(shí)現(xiàn)是SqlDataAdapter 和OleDbDataAdapter。

  通用編程

  假如你很有可能以不同的數(shù)據(jù)源為目標(biāo),并希望將代碼從一種數(shù)據(jù)源移植到另一數(shù)據(jù)源,那么可以考慮對(duì)System.Data名稱空間中的IDbConnection, IDbCommand, IDataReader,和IDbDataAdapter接口進(jìn)行編程。Connection, Command, DataReader, 及 DataAdapter對(duì)象的所有實(shí)現(xiàn)都必須支持這些接口。

  顯示了數(shù)據(jù)訪問(wèn)堆棧及ADO.NET如何與其它數(shù)據(jù)訪問(wèn)技術(shù),包括ADO和OLE DB,聯(lián)系起來(lái)。該圖還顯示了ADO.NET模型中的兩個(gè)治理供給器和主要對(duì)象。 

  關(guān)于ADO到ADO.NET的演化,見(jiàn)MSDN雜志2000年11月期的文章“ADO+簡(jiǎn)介:用于微軟.NET框架的數(shù)據(jù)訪問(wèn)服務(wù)”。 QQread.com 推出各大專業(yè)服務(wù)器評(píng)測(cè) linux服務(wù)器的安全性能 SUN服務(wù)器 HP服務(wù)器 DELL服務(wù)器 IBM服務(wù)器 聯(lián)想服務(wù)器 浪潮服務(wù)器 曙光服務(wù)器 同方服務(wù)器 華碩服務(wù)器 寶德服務(wù)器 存儲(chǔ)過(guò)程與直接SQL的比較

  在本文剩余部分的大部分代碼片段中,都使用了SqlCommand對(duì)象調(diào)用存儲(chǔ)過(guò)程去執(zhí)行數(shù)據(jù)庫(kù)操作。在一些例子中,你見(jiàn)不到SqlCommand對(duì)象,因?yàn)榇鎯?chǔ)過(guò)程名直接傳遞給了SqlDataAdapter對(duì)象,但這仍將導(dǎo)致SqlCommand對(duì)象的創(chuàng)建。

  使用存儲(chǔ)過(guò)程而非SQL語(yǔ)句的原因是:
  • 存儲(chǔ)過(guò)程通常會(huì)使性能增加,因?yàn)閿?shù)據(jù)庫(kù)可以優(yōu)化過(guò)程使用的數(shù)據(jù)訪問(wèn)計(jì)劃,并對(duì)其進(jìn)行緩存以備將來(lái)重用。

  • 在數(shù)據(jù)庫(kù)中,存儲(chǔ)過(guò)程可分別得到保護(hù)。客戶可以被給予執(zhí)行某個(gè)存儲(chǔ)過(guò)程的權(quán)限,但無(wú)權(quán)處理底層的表。

  • 存儲(chǔ)過(guò)程將導(dǎo)致維護(hù)簡(jiǎn)單,因?yàn)樵谝粋€(gè)已部署組件內(nèi),修改存儲(chǔ)過(guò)程通常要比修改硬編碼的SQL語(yǔ)句簡(jiǎn)單。

  • 存儲(chǔ)過(guò)程增加了一個(gè)從底層的數(shù)據(jù)庫(kù)結(jié)構(gòu)中提取出的層。存儲(chǔ)過(guò)程的客戶與存儲(chǔ)過(guò)程的實(shí)現(xiàn)細(xì)節(jié)及底層結(jié)構(gòu)被隔離開(kāi)了。

  • 存儲(chǔ)過(guò)程可以降低網(wǎng)絡(luò)流量,因?yàn)镾QL語(yǔ)句可以以批處理的方式執(zhí)行,而不是從客戶端發(fā)送多個(gè)請(qǐng)求。
  屬性與構(gòu)造函數(shù)的比較

  可以通過(guò)構(gòu)造函數(shù)參數(shù)或直接設(shè)置屬性來(lái)為ADO.NET對(duì)象設(shè)置具體的屬性值。例如,下面的代碼片段在功能上是等同的。

// Use constrUCtor arguments to configure command object
SqlCommand cmd = new SqlCommand( "SELECT * FROM PRODUCTS", conn );
// The above line is functionally equivalent to the following
// three lines which set properties eXPlicitly
sqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT * FROM PRODUCTS";

  從性能角度來(lái)說(shuō),兩種方法的差別可以忽略,因?yàn)樵O(shè)置或獲得.NET對(duì)象的屬性比對(duì)COM對(duì)象執(zhí)行類似操作要有效得多。

  所作出的選擇只是個(gè)人愛(ài)好和編碼風(fēng)格而已。然而,明確地設(shè)置屬性的確使代碼易于理解(非凡是當(dāng)你不熟悉ADO.NET對(duì)象模型時(shí)),便于調(diào)試。

  注重 過(guò)去,VB開(kāi)發(fā)人員被建議避免使用"Dim x As New…"結(jié)構(gòu)創(chuàng)建對(duì)象。在COM環(huán)境中,這些代碼將導(dǎo)致COM對(duì)象創(chuàng)建過(guò)程的“短路”,產(chǎn)生一些奇妙的和不怎么奇妙的錯(cuò)誤。然而,在.NET環(huán)境中,這已不再是一個(gè)問(wèn)題。

  治理數(shù)據(jù)庫(kù)鏈接

  數(shù)據(jù)庫(kù)鏈接是一種危險(xiǎn)的、昂貴的、有限的資源,非凡是在多層Web應(yīng)用程序中。你必須正確治理你的鏈接,因?yàn)槟愕姆椒▽O大的影響應(yīng)用程序的整體升級(jí)性。還有,必須仔細(xì)考慮在哪兒存放鏈接字符串。你需要一個(gè)可配置的、安全的位置。


  在治理數(shù)據(jù)庫(kù)鏈接和鏈接字符串時(shí),你應(yīng)當(dāng)努力:
  • 通過(guò)跨多個(gè)客戶多路復(fù)用一池?cái)?shù)據(jù)庫(kù)鏈接來(lái)幫助實(shí)現(xiàn)應(yīng)用程序的擴(kuò)展性。

  • 采用可配置的、高性能的鏈接池戰(zhàn)略。

  • 在訪問(wèn)SQL Server時(shí)使用微軟Windows操作系統(tǒng)認(rèn)證。

  • 避免中間層的冒充。

  • 安全地存儲(chǔ)鏈接字符串。

  • 較晚地打開(kāi)數(shù)據(jù)庫(kù)鏈接,而較早地關(guān)閉它們。
  本節(jié)討論鏈接池,并幫你選擇合適的鏈接池戰(zhàn)略。其它可選方法也是存在的。本節(jié)也將考慮如何治理、存儲(chǔ)、控制數(shù)據(jù)庫(kù)鏈接字符串。最后,本節(jié)還提供了兩個(gè)編碼方案,使用它們將有助于確保鏈接已可靠關(guān)閉,并返回到鏈接池中。

  鏈接池

  數(shù)據(jù)庫(kù)鏈接池使應(yīng)用程序能夠重用池中的現(xiàn)有鏈接,而不是重復(fù)地建立對(duì)數(shù)據(jù)庫(kù)的鏈接。這種技術(shù)將極大地增加應(yīng)用程序的可擴(kuò)展性,因?yàn)橛邢薜臄?shù)據(jù)庫(kù)鏈接可以為很多的客戶提供服務(wù)。此技術(shù)也將提高性能,因?yàn)槟軌虮苊庥糜诮⑿骆溄拥木薮髸r(shí)間。

  數(shù)據(jù)訪問(wèn)技術(shù),如ODBC和OLE DB,提供了多種形式的鏈接池,它們可配置到不同級(jí)別上。這兩種方式對(duì)數(shù)據(jù)庫(kù)客戶端應(yīng)用程序來(lái)說(shuō)都是透明的。OLE DB鏈接池經(jīng)常被稱為會(huì)話或資源池。

  ADO.NET數(shù)據(jù)供給器提供了透明的鏈接池,每種鏈接池的確切機(jī)制對(duì)每種供給器來(lái)說(shuō)是不同的。本節(jié)討論的鏈接池是關(guān)于:
  • SQL Server .NET數(shù)據(jù)供給器

  • OLE DB .NET數(shù)據(jù)供給器
用SQL Server .NET 數(shù)據(jù)供給器池化

  假如正在使用SQL Server .NET數(shù)據(jù)供給器,那么就可使用該供給器提供的鏈接池化支持特性。它是由供給器在治理代碼內(nèi)內(nèi)置實(shí)現(xiàn)的對(duì)事務(wù)敏感的高效機(jī)制。每個(gè)過(guò)程都將創(chuàng)建池,并且直到過(guò)程結(jié)束,池才被取消。

  你可以透明地使用此種鏈接池,但應(yīng)當(dāng)清楚池是如何被治理的,并要知道可以用哪些選項(xiàng)來(lái)調(diào)整鏈接池。

  如何配置SQL Server .NET數(shù)據(jù)供給器鏈接池

  可以使用一組名稱-值對(duì)以鏈接字符串的形式配置鏈接池。例如,可以配置池是否有效(默認(rèn)是有效的),池的最大、最小容量,用于打開(kāi)鏈接的排隊(duì)請(qǐng)示被阻斷的時(shí)間。下面的示例字符串配置了池的最大和最小容量。

"Server=(local);
Integrated Security=SSPI;
Database=Northwind;
Max Pool Size=75;
Min Pool Size=5"
  當(dāng)鏈接打開(kāi),池被創(chuàng)建時(shí),多個(gè)鏈接增加到池中以使鏈接數(shù)滿足所配置的最小值。此后,鏈接就能增加到池中,直到配置的最大池計(jì)數(shù)。當(dāng)達(dá)到最大計(jì)數(shù)時(shí),打開(kāi)新鏈接的請(qǐng)求將排隊(duì)一段可配置的時(shí)間。

  選擇池容量

  能建立最大極限對(duì)于治理幾千用戶同時(shí)發(fā)出請(qǐng)求的大型系統(tǒng)來(lái)說(shuō)是非常重要的。你需要監(jiān)視鏈接池及應(yīng)用程序的性能,以確定系統(tǒng)的最優(yōu)池容量。最優(yōu)容量還要依靠于運(yùn)行SQL Server的硬件。

  在開(kāi)發(fā)期間,也許需要減小默認(rèn)的最大池容量(目前是100)以幫助查找鏈接泄漏。

  假如設(shè)立了最小池容量,那么當(dāng)池最初被填充以達(dá)到該值時(shí),會(huì)導(dǎo)致一些性能損失,盡管最初鏈接的幾個(gè)客戶會(huì)從中受益。注重,創(chuàng)建新鏈接的過(guò)程被序列化了,這就意味著當(dāng)池最初被填充時(shí),服務(wù)器無(wú)法處理同時(shí)發(fā)生的請(qǐng)求。

  更多信息

  在使用SQL Server .NET數(shù)據(jù)供給器鏈接池時(shí),必須清楚:鏈接是通過(guò)對(duì)鏈接字符串精確匹配的法則被池化的。池化機(jī)制對(duì)名稱-值對(duì)間的空格敏感。例如,下面的兩個(gè)鏈接字符串將生成單獨(dú)的池,因?yàn)榈诙€(gè)字符串包含了一個(gè)額外的空字符。

SqlConnection conn = new SqlConnection( "Integrated Security=SSPI;Database=Northwind");
conn.Open(); // Pool A is created
SqlConmection conn = new SqlConnection( "Integrated Security=SSPI ; Database=Northwind");
conn.Open(); // Pool B is created (extra spaces in string)
 
  • 在.NET框架Beta版中,當(dāng)在調(diào)試器中運(yùn)行時(shí),鏈接池化總是失效了。在調(diào)試器外,對(duì)調(diào)試版和發(fā)行版,鏈接池都能正常運(yùn)作。.NET框架的最終發(fā)行版(RTM)取消了這種限制,鏈接池在所有情況下都能運(yùn)行。

  • 鏈接池被劃分為了多個(gè)特定于事務(wù)的池和一個(gè)用于目前沒(méi)有列在事務(wù)中的多個(gè)鏈接的池。對(duì)于與特定事務(wù)上下文相關(guān)的線程,將從(包含了與事務(wù)建立的鏈接的)合適的池中返回鏈接。這使得使用已建立的鏈接成為透明過(guò)程。
  用OLE DB .NET數(shù)據(jù)供給器池化

  OLE DB .NET數(shù)據(jù)供給器利用OLE DB資源池化的底層服務(wù)將鏈接存儲(chǔ)到池中。很多方法可用于配置資源池化:
  • 可以使用鏈接字符串來(lái)配置、使能資源池化或使其使失效。

  • 可以使用注冊(cè)表

  • 可以通過(guò)程序來(lái)配置資源池化。
  為了避開(kāi)與注冊(cè)表相關(guān)的部署問(wèn)題,應(yīng)避免使用注冊(cè)表配置OLE DB資源池化。

  關(guān)于OLE DB 資源池化的更多細(xì)節(jié),見(jiàn)MSDN中“OLE DB程序員參考”一書的第19章:OLE DB服務(wù)中的資源池化部分。

  用池化對(duì)象治理鏈接池化

  作為Windows DNA開(kāi)發(fā)人員,建議你使OLE DB資源池化和/或ODBC鏈接池化失效,并把COM+對(duì)象池化用作將數(shù)據(jù)庫(kù)鏈接存儲(chǔ)到池中的技術(shù)。這樣做主要出于兩個(gè)原因:
  • 池容量和極限可以(在COM+目錄)被明確配置。


  • 性能提高了。池化對(duì)象的方法可以成倍的勝過(guò)固有池化。
  然而,由于SQL Server .NET數(shù)據(jù)供給器內(nèi)置地使用池化,所以(在使用此供給器時(shí))你不再需要開(kāi)發(fā)自己的對(duì)象池化機(jī)制。這樣就可以避免手工事務(wù)征募帶來(lái)的復(fù)雜性。

  假如正在使用OLE DB .NET數(shù)據(jù)供給器,那么考慮COM+對(duì)象池化以從高級(jí)配置和改進(jìn)的性能中受益。假如你為此目的開(kāi)發(fā)一個(gè)池化對(duì)象,那么必須使用OLE DB資源池化和自動(dòng)事務(wù)征募失效(例如,通過(guò)將“OLE DB Services=-4”包含進(jìn)鏈接字符串中)。必須在池化對(duì)象的實(shí)現(xiàn)中處理事務(wù)征募。

  監(jiān)視鏈接池化

  要監(jiān)視應(yīng)用程序?qū)︽溄映鼗膽?yīng)用情況,可以使用隨SQL Server發(fā)行的Profiler工具,或隨微軟windows 2000發(fā)行的性能監(jiān)視器。

  要利用SQL Server Profiler 監(jiān)視鏈接池化,操作如下:
  1. 單擊開(kāi)始,指向程序,指向Microsoft SQL Server,然后單擊Profiler運(yùn)行Profiler。

  2. 在文件菜單中,指向新建,然后單擊跟蹤。

  3. 提供鏈接內(nèi)容,然后單擊確定。

  4. 在跟蹤屬性對(duì)話框中,單擊事件標(biāo)簽。

  5. 在已選事件類別列表中,確保審核登錄和審核登出事件顯示在安全審核下面。

  6. 單擊運(yùn)行開(kāi)始跟蹤。在鏈接建立時(shí),將會(huì)看到審核登錄事件;在鏈接關(guān)閉時(shí)看到審核登出事件。
  要通過(guò)性能監(jiān)視器監(jiān)視鏈接池化,操作如下:
  1. 單擊開(kāi)始,指向程序,指向治理工具,然后單擊性能運(yùn)行性能監(jiān)視器。

  2. 在圖表背景中右擊,然后單擊增加計(jì)數(shù)器

  3. 在性能對(duì)象下拉列表框中,單擊SQL Server:通用統(tǒng)計(jì)。

  4. 在出現(xiàn)的列表中,單擊用戶鏈接。

  5. 單擊增加,然后單擊關(guān)閉。
  注重 .NET框架的RTM版本將另外包含一組ADO .NET性能計(jì)數(shù)器(這些計(jì)數(shù)器能與性能監(jiān)視器結(jié)合起來(lái)使用),這些計(jì)數(shù)器用于為SQL Server .NET數(shù)據(jù)供給器監(jiān)視并積累鏈接池化狀態(tài)。治理安全性

  盡管數(shù)據(jù)庫(kù)鏈接池化提高了應(yīng)用程序的整體擴(kuò)展性,這也意味著你不再能夠在數(shù)據(jù)庫(kù)端治理安全性。這是因?yàn)闉榱酥С宙溄映鼗溄幼址仨毷窍嗤摹<偃缧枰櫭總€(gè)用戶的數(shù)據(jù)庫(kù)操作,那么考慮為每個(gè)操作增加一個(gè)參數(shù),通過(guò)這個(gè)參數(shù)就可以傳遞用戶身份,手工將用戶活動(dòng)記入數(shù)據(jù)庫(kù)。

  使用Windows 認(rèn)證

  在鏈接到SQL Server時(shí),應(yīng)當(dāng)使用Windows認(rèn)證,因?yàn)樗峁┝嗽S多優(yōu)點(diǎn):
  • 安全性易于治理,因?yàn)槭褂昧藛我?Windows)安全模型而不是分散的SQL Server安全模型。

  • 避免了在鏈接字符串中嵌入用戶名和密碼。

  • 用戶名和密碼不是以明文方式在網(wǎng)絡(luò)中傳輸?shù)摹?

  • 通過(guò)密碼過(guò)期期限,最小長(zhǎng)度,多次無(wú)效登錄請(qǐng)求后帳號(hào)鎖定提高了登錄的安全性。

    性能

    .NETBeta 2版的性能測(cè)試表明,使用Windows認(rèn)證與使用SQL Server認(rèn)證相比,要花費(fèi)更多的時(shí)間才能打開(kāi)池化的數(shù)據(jù)庫(kù)鏈接。然而,盡管Windows認(rèn)證的成本較高,但與執(zhí)行一個(gè)命令或存儲(chǔ)過(guò)程所花費(fèi)的時(shí)間相比,其(引起的)性能損失相對(duì)來(lái)說(shuō)并不重要。結(jié)果,上面所列出的Windows認(rèn)證的優(yōu)點(diǎn)通常會(huì)稍微超過(guò)性能損失。

    同樣,當(dāng)打開(kāi)一個(gè)池化鏈接時(shí),在.NET框架的RTM版本中,Windows認(rèn)證與SQL Server認(rèn)證的差別有望變得更不明顯。

      避免在中間層中冒充

      Windows認(rèn)證需要訪問(wèn)數(shù)據(jù)庫(kù)的Windows帳號(hào)。雖然看上去在中間層中使用冒充更符合邏輯,但必須避免這樣做,因?yàn)閾p害鏈接池化并對(duì)應(yīng)用程序的擴(kuò)展性產(chǎn)生嚴(yán)重影響。

      為了解決這個(gè)問(wèn)題,考慮對(duì)有限的Windows帳號(hào)(而不是被認(rèn)證的負(fù)責(zé)人)實(shí)施冒充,每個(gè)帳號(hào)代表一個(gè)特定的角色。

      例如,可以考慮下面的方法:
  • 創(chuàng)建兩個(gè)Windows帳號(hào),一個(gè)用于讀操作,一個(gè)用于寫操作(也可以用單獨(dú)的帳號(hào)映射針對(duì)特定應(yīng)用程序的角色。例如,可以為互聯(lián)網(wǎng)用戶使用一個(gè)帳號(hào),而為內(nèi)部操作員和/或治理員使用另外的帳號(hào))。

  • 將每個(gè)帳號(hào)映射到一個(gè)SQL Server數(shù)據(jù)庫(kù)角色,然后為每個(gè)角色設(shè)置所需的數(shù)據(jù)庫(kù)權(quán)限。

  • 在數(shù)據(jù)訪問(wèn)層中使用應(yīng)用程序邏輯確定執(zhí)行數(shù)據(jù)庫(kù)操作時(shí),哪個(gè)Windows帳號(hào)需要冒充。
  注重 每個(gè)帳號(hào)必須是同一域或信任域中在Internet信息服務(wù)(IIS)和SQL Server中存在的域帳號(hào);也可以是在每臺(tái)計(jì)算機(jī)上創(chuàng)建(具有相同用戶名和密碼)的匹配帳號(hào)。

  為網(wǎng)絡(luò)庫(kù)使用TCP/ip

  SQL Server 7.0及其以后版本支持用于所有網(wǎng)絡(luò)庫(kù)的Windows認(rèn)證。使用TCP/IP可以獲得配置、性能及擴(kuò)展性優(yōu)點(diǎn)。關(guān)于使用TCP/IP的更多信息,見(jiàn)本文通過(guò)防火墻建立鏈接一節(jié)。

  存儲(chǔ)鏈接字符串

  有多種方法可存儲(chǔ)鏈接字符串,每種方法具有不同程度的靈活性和安全性。盡管在源代碼中對(duì)字符串進(jìn)行硬編碼提供了最優(yōu)性能,但文件系統(tǒng)緩存確保了與在文憑系統(tǒng)外部存儲(chǔ)字符串相關(guān)的性能損失可被忽略。實(shí)際上外部鏈接字符串(答應(yīng)治理員進(jìn)行配置)所提供的附加靈活性在任何情況下都是受歡迎的。

  選擇存儲(chǔ)鏈接字符串的方法時(shí),首先要考慮的兩個(gè)重要因素是配置的安全性與簡(jiǎn)易性,其次是性能。

  可以選擇將數(shù)據(jù)庫(kù)鏈接字符串存儲(chǔ)在下列位置:
  • 應(yīng)用程序配置文件 例如用于asp.net Web應(yīng)用程序的Web.config文件。


  • 通用數(shù)據(jù)鏈接文件(UDL) (只被OLE DB .NET 數(shù)據(jù)供給器所支持)

  • Windows 注冊(cè)表

  • 定制文件

  • COM+ 目錄,通過(guò)過(guò)使用構(gòu)造字符串(只用于服務(wù)組件)
  使用Windows認(rèn)證訪問(wèn)SQL Server,就可以避免在鏈接字符串存儲(chǔ)用戶名和密碼。假如 安全需求要求更嚴(yán)格的方式,那么就考慮以加密格式存儲(chǔ)鏈接字符串。

  對(duì)于ASP.NET Web應(yīng)用程序,以加密格式將鏈接字符串存儲(chǔ)在Web.config文件中是一種安全而可配置的解決方案。

  注重,在鏈接字符串中將Persist Security Info命名值設(shè)置為假,就可以阻止利用SqlConnection 或OleDbConnection對(duì)象的ConnectionString屬性返回對(duì)安全敏感的內(nèi)容,如密碼。

  下面幾個(gè)小節(jié)討論了如何用這些方法存儲(chǔ)鏈接字符串,并說(shuō)明了相對(duì)的優(yōu)點(diǎn)和缺點(diǎn)。這使你能根據(jù)特定的應(yīng)用程序環(huán)境作出相應(yīng)的的選擇。

  使用XML應(yīng)用程序配置文件

  可以使用元素appSettings將數(shù)據(jù)庫(kù)鏈接字符串存儲(chǔ)在應(yīng)用程序配置文件的定制設(shè)置部分。該元素支持任意要害字-值對(duì),如下面的代碼片段所示:

value="server=(local);Integrated Security=SSPI;database=northwind"/>

  注重:appSettings元素現(xiàn)在在configuration元素下面,并且不能直接出現(xiàn)在system.web下面。

  優(yōu)點(diǎn)
  • 易于部署。通過(guò)常規(guī).NET xcopy部署,鏈接字符串隨配置文件一起被部署。

  • 通過(guò)程序易于訪問(wèn)。ConfigurationSettings類的AppSettings屬性使得在運(yùn)行時(shí)讀取數(shù)據(jù)庫(kù)鏈接字符串更為簡(jiǎn)單。

  • 支持動(dòng)態(tài)更新(僅限于ASP.NET)。假如治理員更新了Web.config文件中的鏈接字符串,那么下次在字符串被訪問(wèn)時(shí)所作出的變化生效,這對(duì)一個(gè)無(wú)狀態(tài)的組件來(lái)說(shuō),就象客戶再次利用組件作出了數(shù)據(jù)訪問(wèn)請(qǐng)求一樣。
  缺點(diǎn)

  安全性。盡管ASP.NET Internet 服務(wù)器應(yīng)用程序編程接口(ISAPI)DLL阻止了客戶直接訪問(wèn)帶.config擴(kuò)展名的文件,并且NTFS文件系統(tǒng)權(quán)限也用于進(jìn)一步限制訪問(wèn),但你可能仍希望避免以明文方式將這些內(nèi)容存儲(chǔ)在前端的Web服務(wù)器上。要增加安全性,需將鏈接字符串以加密格式存儲(chǔ)在配置文件中。

  更多信息

  利用System.Configuration.ConfigurationSettings類的AppSettings靜態(tài)屬性,可以獲取應(yīng)用程序的定制設(shè)置。如下面的代碼片段所示,此處假定先前示例的定置要害字為DBConnStr。

using System.Configuration;
private string GetDBaseConnectionString()
{
   return ConfigurationSettings.AppSettings["DBConnStr"];
} QQread.com 推出各大專業(yè)服務(wù)器評(píng)測(cè) Linux服務(wù)器的安全性能 SUN服務(wù)器 HP服務(wù)器 DELL服務(wù)器 IBM服務(wù)器 聯(lián)想服務(wù)器 浪潮服務(wù)器 曙光服務(wù)器 同方服務(wù)器 華碩服務(wù)器 寶德服務(wù)器 使用UDL文件

  OLE DB .NET數(shù)據(jù)供給器支持在它的鏈接字符串中使用統(tǒng)一數(shù)據(jù)鏈接(UDL)文件名。可以以構(gòu)建參數(shù)的形式將鏈接字符串傳給OleDbConnection對(duì)象,或利用對(duì)象的ConnectionString屬性設(shè)置鏈接字符串。

  注重 SQL Server .NET數(shù)據(jù)供給器不支持在它的鏈接字符串中使用UDL文件。因此,只有使用OLE DB .NET數(shù)據(jù)供給器,此方法才有效。

  對(duì)于OLE DB 供給器,要利用鏈接字符串引用UDL文件,使用“File Name=name.udl.”。

  優(yōu)點(diǎn)

  標(biāo)準(zhǔn)方法。你也許已經(jīng)在用UDL文件進(jìn)行鏈接字符串的治理了。

  缺點(diǎn)
  • 性能。每次打開(kāi)鏈接時(shí),包含UDLs的鏈接字符串都被讀取并被解析。

  • 安全性。UDL文件以純文本格式存儲(chǔ)。利用NFTS文件權(quán)限可以確保這些文件的安全性,但這樣做將引發(fā)與使用.config文件相同的問(wèn)題。

  • SqlClient對(duì)象不支持UDL文件。此方法不被 SQL Server .NET數(shù)據(jù)供給器所支持,而你要用此供給器訪問(wèn) SQL Server 7.0及其以后版本。
  更多信息
  • 必須確保治理員擁有該文件的讀/寫訪問(wèn)權(quán)限以便進(jìn)行治理,并且還要確保運(yùn)行應(yīng)用程序的身份擁有讀權(quán)限。對(duì)于ASP.NET Web應(yīng)用程序,應(yīng)用程序工作者進(jìn)程默認(rèn)是以SYSTEM帳號(hào)運(yùn)行的,但利用機(jī)器范圍的配置文件(Machine.config)中的元素可以將其覆蓋掉。利用Web.config文件中的元素,及一個(gè)可選的指定帳號(hào),可以進(jìn)行冒充。

  • 對(duì)于Web應(yīng)用程序,要確保沒(méi)有將UDL文件放在虛目錄中,因?yàn)槟菢訒?huì)使該文件可通過(guò)網(wǎng)絡(luò)下載。

  使用Windows注冊(cè)表

  可以利用定制要害字將鏈接字符串存儲(chǔ)在Windows注冊(cè)表中,但由于部署問(wèn)題,建議不要使用。

  優(yōu)點(diǎn)
  • 安全性。利用訪問(wèn)控制列表(ACLs),可以對(duì)所選的注冊(cè)表要害字的訪問(wèn)進(jìn)行治理。對(duì)更高級(jí)別的安全性,考慮對(duì)數(shù)據(jù)進(jìn)行加密。

  • 通過(guò)程序易于訪問(wèn)。.NET類支持從注冊(cè)表中讀取字符串。
  缺點(diǎn)

  • 部署。相關(guān)的注冊(cè)表設(shè)置必須同應(yīng)用程序一起部署,從某種程度上抵消了xcopy部署的優(yōu)點(diǎn)。
  使用定置文件

  可以使用定制文件來(lái)存儲(chǔ)鏈接字符串,然而這種技術(shù)沒(méi)有優(yōu)點(diǎn),因此并不推薦使用。

  優(yōu)點(diǎn)
  • 沒(méi)有
  缺點(diǎn)
  • 額外編碼。這種方法需要額外編碼,并迫使你明確處理同時(shí)發(fā)生的問(wèn)題。

  • 部署。此文件必須同其它ASP.NET應(yīng)用程序文件一起拷貝。避免將此文件放在ASP.NET應(yīng)用程序的目錄或子目錄中,就可以阻止通過(guò)網(wǎng)絡(luò)對(duì)其進(jìn)行下載。
  使用構(gòu)建參數(shù)和COM+目錄

  可以將鏈接字符串存儲(chǔ)在COM+目錄中,并利用對(duì)象的構(gòu)造字符串將它自動(dòng)地傳遞給對(duì)象。COM+在初始化對(duì)象,提供配置構(gòu)造字符串后,將立即調(diào)用對(duì)象的Construct方法。

  注重這個(gè)方法只用于服務(wù)組件。只有治理組件使用了其它服務(wù),如分布式事務(wù)處理支持或?qū)ο蟪鼗瘯r(shí),才考慮使用此方法。

  優(yōu)點(diǎn)
  • 治理性。利用組件服務(wù)MMC插件,治理員可以很方便地配置鏈接字符串。

    缺點(diǎn)
  • 安全性。COM+目錄被認(rèn)為是一個(gè)不安全的存儲(chǔ)區(qū)(雖然利用COM+角色你可以限制對(duì)它的訪問(wèn)),并因此不能用于以明文維護(hù)鏈接字符串。

  • 部署。COM+目錄中的條目必須隨.NET應(yīng)用程序一同部署。假如使用了其它企業(yè)服務(wù),如分布式事務(wù)或?qū)ο蟪鼗敲磳?shù)據(jù)庫(kù)鏈接字符串存儲(chǔ)在目錄中不會(huì)增加部署的額外開(kāi)銷,因?yàn)橐С制渌?wù),必須部署COM+目錄。

  • 必須為組件提供服務(wù)。可以只為所服務(wù)的組件使用構(gòu)造字符串。要使能構(gòu)造字符串,不能簡(jiǎn)單地從ServicedComponent類中派生所需組件類(這將為組件提供服務(wù))。
  鏈接使用方式

  不管何種.NET數(shù)據(jù)供給器,你必須總是:
  • 盡可能晚地打開(kāi)數(shù)據(jù)庫(kù)鏈接。

  • 以盡可能短的時(shí)間使用該鏈接。

  • 盡可能快地關(guān)閉該鏈接。鏈接直到通過(guò)Close或Dispose方法關(guān)閉后,它才返回到池中。即使發(fā)現(xiàn)它處于崩潰狀態(tài),也應(yīng)當(dāng)關(guān)閉它。這樣做確保了它能返回池中,并被標(biāo)記為無(wú)效。對(duì)象池周期性地掃描池,以查找已被標(biāo)記為無(wú)效的對(duì)象。
  為確保在方法返回前鏈接已經(jīng)關(guān)閉,考慮使用下面兩個(gè)代碼片段中演示的方法。第一個(gè)示例使用了finally塊,第二個(gè)示例使用了C# using聲明,此聲明確保了對(duì)象的Dispose方法被調(diào)用。

  下面的代碼確保finally塊關(guān)閉了鏈接。注重,此方法只用于Visual Basic .NET及C#中,因?yàn)閂isual Basic .NET支持結(jié)構(gòu)化例外處理。

public void DoSomeWork()
{
 SqlConnection conn = new SqlConnection(connectionString);
 SqlCommand cmd = new SqlCommand("CommandProc", conn );
 cmd.CommandType = CommandType.StoredProcedure;
 try
 {
  conn.Open();
  cmd.ExecuteNonQuery();
  }
 catch (Exception e)
 {
  // Handle and log error
  }
 finally
 {
  conn.Close();
  }
}
  現(xiàn)在的代碼顯示了另外一種方法,此方法使用了C# using聲明。注重,Visual Basic .NET并不支持using聲明,或任何功能相同的對(duì)應(yīng)語(yǔ)句。

public void DoSomeWork()
{
// using guarantees that Dispose is called on conn, which will
// close the connection.
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand("CommandProc", conn);
fcmd.CommandType = CommandType.StoredProcedure;
conn.Open();
cmd.ExecuteQuery();
}
}
  此方法也適用于其它對(duì)象,如SqlDataReader 或OleDbDataReader,在其它任何對(duì)象對(duì)當(dāng)前鏈接進(jìn)行處理前,這些對(duì)象必須被關(guān)閉。錯(cuò)誤處理

  ADO.NET錯(cuò)誤生成后,將由.NET框架內(nèi)置的底層結(jié)構(gòu)化異常處理支持所處理。結(jié)果,在數(shù)據(jù)訪問(wèn)代碼中的錯(cuò)誤處理方式與應(yīng)用程序中其它地方的錯(cuò)誤處理方式完全相同。通過(guò)標(biāo)準(zhǔn)的.NET異常處理語(yǔ)法和技術(shù),異常被檢測(cè)到并被處理。

  本節(jié)描述了如何開(kāi)發(fā)強(qiáng)壯的數(shù)據(jù)訪問(wèn)代碼,并解釋了如何處理數(shù)據(jù)訪問(wèn)錯(cuò)誤。本節(jié)還提供了與SQL Server .NET數(shù)據(jù)供給器相關(guān)的異常處理詳盡指南。

  .NET 異常

  .NET數(shù)據(jù)供給器將特定的數(shù)據(jù)庫(kù)的錯(cuò)誤狀態(tài)轉(zhuǎn)化為標(biāo)準(zhǔn)的異常類型,應(yīng)當(dāng)在數(shù)據(jù)訪問(wèn)代碼中對(duì)這些異常進(jìn)行處理。通過(guò)相關(guān)的異常對(duì)象的屬性,可以獲得特定數(shù)據(jù)庫(kù)的錯(cuò)誤細(xì)節(jié)。

  所有.NET異常類型最終是從System名稱空間的Exception基類中派生的。.NET數(shù)據(jù)供給器釋放特定的供給器異常類型。例如,一旦SQL Server 返回一個(gè)錯(cuò)誤狀態(tài)時(shí),SQL Server .NET數(shù)據(jù)供給器釋放SqlException對(duì)象。類似的,OLE DB .NET數(shù)據(jù)供給器釋放 OleDbException類型的異常,此對(duì)象包含了由底層OLE DB供給器暴露的細(xì)節(jié)。

  圖3顯示了.NET數(shù)據(jù)供給器異常的層次結(jié)構(gòu)。注重,OleDbException類是從 ExternalException類派生的ExternalException類是所有COM例外的基類。對(duì)象的ErrorCode屬性存儲(chǔ)了OLE DB生成的COM HRESULT。 


  緩存并處理.NET異常

  要處理數(shù)據(jù)訪問(wèn)例外狀態(tài),將數(shù)據(jù)訪問(wèn)代碼放在try塊中,并在catch塊中利用合適的過(guò)濾器捕捉生成的任何例外。例如,當(dāng)利用SQL Server .NET數(shù)據(jù)供給器編寫數(shù)據(jù)訪問(wèn)代碼時(shí),應(yīng)當(dāng)捕捉SqlException類型的異常,如下面的代碼所示:

try
{
// Data access code
}
catch (SqlException sqlex) // more specific
{
}
catch (Exception ex) // less specific
{
}
  假如為不止一個(gè)catch聲明提供了不同的過(guò)濾標(biāo)準(zhǔn),記住,按最非凡類型到最不非凡類型的順序排列它們。通過(guò)這種方式,catch塊中最非凡類型將將為任何給定的類型所執(zhí)行。

  SqlException 類所暴露的屬性包含了例外狀態(tài)的細(xì)節(jié)。其中包括:
  • Message屬性,它包含了用于描述錯(cuò)誤的文本。

  • Number屬性,它包含唯一標(biāo)識(shí)錯(cuò)誤類型的錯(cuò)誤號(hào)。

  • State屬性。它包含了關(guān)于錯(cuò)誤啟用狀態(tài)的附加信息。它經(jīng)常用于指示非凡錯(cuò)誤狀態(tài)的某個(gè)特定事件。例如,假如單一存儲(chǔ)過(guò)程從不止一行中生成同樣的錯(cuò)誤,那么本屬性將用于標(biāo)識(shí)某個(gè)具體的事件。

  • Errors集合。它包含了SQL Server生成的錯(cuò)誤的具體信息。此集合部是包含至少一個(gè)SqlError類型的對(duì)象。
  下面的代碼片段演示了如何利用SQL Server .NET數(shù)據(jù)供給器處理SQL Server 錯(cuò)誤狀態(tài):

using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
// Method exposed by a Data Access Layer (DAL) Component
public string GetProductName( int ProductID )
{
 SqlConnection conn = new SqlConnection("server=(local);
    Integrated Security=SSPI;database=northwind");
 // Enclose all data access code within a try block
 try
 {
  conn.Open();
  SqlCommand cmd = new SqlCommand("LookupProductName", conn );
  cmd.CommandType = CommandType.StoredProcedure;

  cmd.Parameters.Add("@ProductID", ProductID );
  SqlParameter paramPN = cmd.Parameters.Add("@ProductName", SqlDBType.VarChar, 40 );
  paramPN.Direction = ParameterDirection.Output;

  cmd.ExecuteNonQuery();
  // The finally code is executed before the method returns
  return paramPN.Value.ToString();
 }
 catch (SqlException sqlex)
 {
  // Handle data access exception condition
  // Log specific exception details
  LogException(sqlex);
  // Wrap the current exception in a more relevant
  // outer exception and re-throw the new exception
  throw new DALException("Unknown ProductID: " + ProductID.ToString(), sqlex );
 }
 catch (Exception ex)
 {
  // Handle generic exception condition . . .
  throw ex;
 }
 finally
 {
  conn.Close(); // Ensures connection is closed
 }
}

// Helper routine that logs SqlException details to the
// Application event log
private void LogException( SqlException sqlex )
{
 EventLog el = new EventLog();
 el.Source = "CustomAppLog";
 string strMessage;
 strMessage = "Exception Number : " + sqlex.Number + "(" + sqlex.Message + ") has occurred";
 el.WriteEntry( strMessage );

 foreach (SqlError sqle in sqlex.Errors)
 {
  strMessage = "Message: " + sqle.Message +" Number: " + sqle.Number + " Procedure: " +         sqle.Procedure + " Server: " + sqle.Server + " Source: " + sqle.Source +
     " State: " + sqle.State + " Severity: " + sqle.Class +
     " LineNumber: " + sqle.LineNumber;
  el.WriteEntry( strMessage );
 }
}


  在SqlException catch塊中,代碼最初利用LogException幫助函數(shù)記錄錯(cuò)誤狀態(tài),此函數(shù)利用foreach聲明枚舉了Errors集合中特定于供給器的細(xì)節(jié),并將錯(cuò)誤細(xì)節(jié)記錄到錯(cuò)誤日志中。 Catch塊中的代碼然后將特定于SQL Server的例外封裝在DALException類型的對(duì)象中,這樣做對(duì)調(diào)用者的GetProductName方法更具有意義。例外處理程序使用要害字throw將例外傳回調(diào)用者。 QQread.com 推出各大專業(yè)服務(wù)器評(píng)測(cè) Linux服務(wù)器的安全性能 SUN服務(wù)器 HP服務(wù)器 DELL服務(wù)器 IBM服務(wù)器 聯(lián)想服務(wù)器 浪潮服務(wù)器 曙光服務(wù)器 同方服務(wù)器 華碩服務(wù)器 寶德服務(wù)器 從存儲(chǔ)過(guò)程中生成錯(cuò)誤

  T-SQL提供了一個(gè)RAISERROR(注重拼寫)函數(shù)。你可用此函數(shù)生成定置錯(cuò)誤,并將錯(cuò)誤返回客戶。對(duì)于ADO.NET客戶,SQL Server .NET數(shù)據(jù)供給器對(duì)這些數(shù)據(jù)錯(cuò)誤進(jìn)行解釋,并把它們轉(zhuǎn)化為SqlError對(duì)象。

  使用RAISERROR函數(shù)是簡(jiǎn)單地方法是將消息文本作為第一個(gè)參數(shù)包括進(jìn)來(lái),然后指定嚴(yán)重及狀態(tài)參數(shù),如下面的代碼片段所示: RAISERROR( 'Unknown Product ID: %s', 16, 1, @ProductID )

  在這個(gè)例子中,替代參數(shù)用于將當(dāng)前產(chǎn)品ID作為錯(cuò)誤消息文本的一部分返回,參數(shù)2是消息的嚴(yán)重性,參數(shù)3是消息狀態(tài)。

  更多信息
  • 為了避免對(duì)消息文本進(jìn)行硬編碼,你可以利用sp_addmessage系統(tǒng)存儲(chǔ)過(guò)程或SQL Server 企業(yè)治理器將你自己的消息增加到sysmessages表中。然后你就可以使用傳遞到RAISERROR函數(shù)的ID引用消息了。你所定義的消息Ids必須大于50000,如下代碼片段所示:

  • RAISERROR( 50001, 16, 1, @ProductID )

  • 關(guān)于RAISERROR函數(shù)的完整細(xì)節(jié),請(qǐng)?jiān)赟QL Server的在線書目中查詢RAISERROR。
  正確使用嚴(yán)重性等級(jí)

  仔細(xì)選擇錯(cuò)誤嚴(yán)重性等級(jí),并要清楚每個(gè)級(jí)別造成的沖擊。錯(cuò)誤嚴(yán)重性等級(jí)的范圍是0-25,并且它用于指出SQL Server 2000所碰到的問(wèn)題的類型。在客戶端代碼中,通過(guò)在SqlException類的Errors集合中檢查SqlError對(duì)象的 Class屬性,你可以獲得錯(cuò)誤的嚴(yán)重性。表1 指出了不同嚴(yán)重性等級(jí)的意義及所造成的沖擊。

  表1.錯(cuò)誤嚴(yán)重性等級(jí)--沖擊及意義

嚴(yán)重性等級(jí) 鏈接已關(guān)閉 生成SqlException對(duì)象
意義
10及其以下  No No 通知型消息,并不表示犯錯(cuò)誤狀態(tài)。 11-16 No Yes 可由用戶修改的錯(cuò)誤,例如,使用修改后的輸入數(shù)據(jù)重試操作。 17-19 No Yes 資源或系統(tǒng)錯(cuò)誤。 20-25 Yes Yes 致命的系統(tǒng)錯(cuò)誤(包括硬件錯(cuò)誤)。客戶鏈接被終止。
  控制自動(dòng)化事務(wù)

  SQL Server .NET數(shù)據(jù)供給器對(duì)它所碰到的任何嚴(yán)重性大于10的錯(cuò)誤都拋出SqlException對(duì)象。當(dāng)作為自動(dòng)化(COM+)事務(wù)一部分的組件檢測(cè)到SqlException對(duì)象后,該組件必須確保它能取消事務(wù)。這也許是,也許不是自動(dòng)化過(guò)程,并要依靠該方法是否已經(jīng)對(duì)AutoComplete屬性作出了標(biāo)記。

  關(guān)于在自動(dòng)化事務(wù)上下文中處理對(duì)象的更多信息,見(jiàn)本文中的確定事務(wù)結(jié)果一節(jié)。

  得到通知型消息

  10及其以下嚴(yán)重性等級(jí)用于表示通知型消息,并且不會(huì)引發(fā)SqlException對(duì)象的拋出。

  要獲得通知型消息:
  • 創(chuàng)建事件處理程序,并提交給SqlConnection對(duì)象所暴露的InfoMessage事件。下面的代碼片段顯示了事件代理。
public delegate void SqlInfoMessageEventHandler( object sender,
SqlInfoMessageEventArgs e );
  通過(guò)傳遞到你的事件處理處理程序中的SqlInfoMessageEventArgs對(duì)象,可以得到消息數(shù)據(jù)。此對(duì)象暴露了Errors屬性,該屬性包含一組SqlError對(duì)象--每個(gè)通知消息一個(gè)SqlError對(duì)象。下面的代碼片段演示了如何注冊(cè)用于記錄通知型消息的事件處理程序。

public string GetProductName( int ProductID )
{
SqlConnection conn = new SqlConnection(
"server=(local);Integrated Security=SSPI;database=northwind");
try
{
// Register a message event handler
conn.InfoMessage += new SqlInfoMessageEventHandler( MessageEventHandler );
conn.Open();
// Setup command object and execute it
. . .
}
catch (SqlException sqlex)
{
// log and handle exception
. . .
}
finally
{
conn.Close();
}
}
// message event handler
void MessageEventHandler( object sender, SqlInfoMessageEventArgs e )
{
foreach( SqlError sqle in e.Errors )
{
// Log SqlError properties
. . .
}
}
  性能

  本節(jié)介紹了一些常見(jiàn)的數(shù)據(jù)訪問(wèn)方案,對(duì)每種方案,以ADO.NET 數(shù)據(jù)訪問(wèn)代碼的形式描述了最優(yōu)性能和擴(kuò)展性解決方案。在合適的地方,還對(duì)性能,功能及開(kāi)發(fā)最作出了比較。本節(jié)考慮了下面的功能方案。
  • 獲取多行. 獲取一個(gè)結(jié)果集,并在得到的行中重復(fù)。


  • 獲取一行. 獲取具有指定要害字的一行。

  • 獲取一項(xiàng). 從指定的行中得到一項(xiàng)。

  • 確定某項(xiàng)數(shù)據(jù)的存在性. 檢查具有特定要害字的一行是否存在。這是單項(xiàng)查找方案的一種變體,這里返回一個(gè)簡(jiǎn)單的布爾值就足夠了。
  獲取多行

  在這個(gè)方案中,你要獲取一組表格化數(shù)據(jù),并在得到的行中重復(fù)執(zhí)行某個(gè)操作。例如你得到了一組數(shù)據(jù),并以非鏈接的方式處理,然后(可能通過(guò)Web服務(wù))將它作為XML文檔傳遞給客戶應(yīng)用程序。可選的,你也可以以Html表的形式將這些數(shù)據(jù)顯示出來(lái)。

  為了幫助確定最合適的數(shù)據(jù)訪問(wèn)方法,考慮你是否需要(非鏈接)DataSet 對(duì)象的附加靈活性,還是只需要SqlDataReader對(duì)象提供的原有性能,這些性能非常適合于B2C Web應(yīng)用程序的數(shù)據(jù)表示。圖4顯示了這兩種基本場(chǎng)景。

  注重用于填充DataSet的SqlDataAdapter利用SqlDataReader方法數(shù)據(jù)。 


  方法比較

  當(dāng)從數(shù)據(jù)源中獲取多行時(shí),你可以使用下面的方法:
  • 使用SqlDataAdapter對(duì)象生成DataSet 或 DataTabl對(duì)象。

  • 利用SqlDataReader對(duì)象提供只讀的只向前的數(shù)據(jù)流。

  • 利用XmlReader對(duì)象提供只讀的只向前的XML數(shù)據(jù)流。
  SqlDataReader 與 DataSet/DataTable間的選擇本質(zhì)上是性能與功能間的選擇。SqlDataReader 提供了最優(yōu)性能,而DataSet提供了額外的功能與靈活性。 QQread.com 推出各大專業(yè)服務(wù)器評(píng)測(cè) Linux服務(wù)器的安全性能 SUN服務(wù)器 HP服務(wù)器 DELL服務(wù)器 IBM服務(wù)器 聯(lián)想服務(wù)器 浪潮服務(wù)器 曙光服務(wù)器 同方服務(wù)器 華碩服務(wù)器 寶德服務(wù)器 數(shù)據(jù)綁定

  所有這三個(gè)對(duì)象都可以作為數(shù)據(jù)綁定控件的數(shù)據(jù)源。而DataSet 和 DataTable 可作為更廣范圍控件的數(shù)據(jù)源。這是因?yàn)镈ataSet 和 DataTable 實(shí)現(xiàn)了(生成Ilist接口)IlistSource接口,而SqlDataReader 實(shí)現(xiàn)了Ienumerable接口。許多能進(jìn)行數(shù)據(jù)綁定的WinForm控件需要實(shí)現(xiàn)了Ilist接口的數(shù)據(jù)源。

  這種不同是因?yàn)闉槊糠N對(duì)象類型設(shè)計(jì)的場(chǎng)景類型不同。DataSet (它包含 DataTable)是一個(gè)豐富的、非鏈接結(jié)構(gòu),它適合于Web和桌面(WinForm)應(yīng)用程序。另一方面,數(shù)據(jù)閱讀器已經(jīng)為Web應(yīng)用程序進(jìn)行了優(yōu)化,這種應(yīng)用程序需要優(yōu)化的、只能向前的數(shù)據(jù)訪問(wèn)。

  檢查將要綁定到的特定控件類型的數(shù)據(jù)源需求。

  在應(yīng)用程序?qū)娱g傳遞數(shù)據(jù)

  DataSet提供了可作為XML被任意操縱數(shù)據(jù)的關(guān)系圖,并答應(yīng)數(shù)據(jù)的非鏈接緩存拷貝在應(yīng)用程序?qū)优c組件間傳遞。然而,SqlDataReader提供了更優(yōu)化的性能,因?yàn)樗苊饬伺c創(chuàng)建DataSet相關(guān)的性能及內(nèi)存開(kāi)銷。記住,DataSet對(duì)象的創(chuàng)建將導(dǎo)致多個(gè)子對(duì)象--包括DataTable, DataRow 和DataColumn--及作為這些子對(duì)象容器的集合對(duì)象的創(chuàng)建。

  使用DataSet

  使用SqlDataAdapter填充的DataSet對(duì)象,當(dāng):
  • 你需要非鏈接的駐留內(nèi)存的緩存數(shù)據(jù),以便你能將它傳遞到其它組件或應(yīng)用程序中的其它層。

  • 你需要內(nèi)存中的數(shù)據(jù)關(guān)系圖以執(zhí)行XML或非XML操作。

  • 你正在使用的數(shù)據(jù)來(lái)自多個(gè)數(shù)據(jù)源,如多個(gè)數(shù)據(jù)庫(kù)、表或文件。

  • 你希望更新獲得的一些或所有行,并希望利用SqlDataAdapter的批更新功能。

  • 你要對(duì)控件綁定數(shù)據(jù),而此控件需要支持IList接口的數(shù)據(jù)源。
  更多信息

  假如使用SqlDataAdapter生成DataSet 或 DataTable,需注重:
  • 不必明確打開(kāi)或關(guān)閉數(shù)據(jù)庫(kù)鏈接。SqlDataAdapter Fill方法打開(kāi)數(shù)據(jù)庫(kù)鏈接,并在此方法返回前關(guān)閉該鏈接。假如鏈接原來(lái)已經(jīng)打開(kāi),那么此方法仍使鏈接處于打開(kāi)狀態(tài)。

  • 假如出于其它目的需要鏈接,那么考慮在調(diào)用Fill方法前打開(kāi)鏈接。這樣你就可以避免不必要的打開(kāi)/關(guān)閉操作,提高性能。

  • 盡管能重復(fù)使用同一SqlCommand對(duì)象多執(zhí)行同樣的命令,但不要重復(fù)使用此對(duì)象執(zhí)行不同的命令。
  使用SqlDataReader

  些劣情況,可以使用通過(guò)調(diào)用 SqlCommand 對(duì)象的ExecuteReader方法得到的SqlDataReader對(duì)象:
  • 正在處理大量數(shù)據(jù)時(shí)--太多了而不能在單個(gè)緩沖區(qū)內(nèi)維護(hù)。

  • 希望減少應(yīng)用程序在內(nèi)存中的印跡。

  • 希望避免與DataSet對(duì)象創(chuàng)建相關(guān)的開(kāi)銷。

  • 希望對(duì)某控件執(zhí)行數(shù)據(jù)綁定操作,而此控件支持實(shí)現(xiàn)了IEnumerable接口的數(shù)據(jù)源。

  • 希望流水線化數(shù)據(jù)訪問(wèn),并對(duì)其優(yōu)化。

  • 正在讀取包含二進(jìn)制大對(duì)象(BLOB)列的行。你可以使用SqlDataReader對(duì)象以可治理的大塊為單位從數(shù)據(jù)庫(kù)中將BLOB數(shù)據(jù)拉出來(lái),而不是一次性地將所有數(shù)據(jù)提取出來(lái)。關(guān)于處理BLOB數(shù)據(jù)的更多細(xì)節(jié),見(jiàn)本文處理BLOBs一節(jié)。
  更多信息

  假如使用SqlDataReader對(duì)象,請(qǐng)注重:
  • 在數(shù)據(jù)閱讀器活動(dòng)期間,底層的數(shù)據(jù)庫(kù)鏈接保持打開(kāi),并不能用于其它任何目的。盡可能早地對(duì)SqlDataReader對(duì)象調(diào)用Close方法。

  • 每個(gè)鏈接只能有一個(gè)數(shù)據(jù)閱讀器。

  • 通過(guò)向ExecuteReader方法傳遞CommandBehavior.CloseConnection枚舉值,可以在使用完數(shù)據(jù)閱讀器后,明確地關(guān)閉鏈接;或者,將鏈接生命周期綁定到SqlDataReader對(duì)象。這預(yù)示著當(dāng)SqlDataReader對(duì)象關(guān)閉時(shí),鏈接也將關(guān)閉。

  • 在利用閱讀器訪問(wèn)數(shù)據(jù)時(shí),假如你知道列的底層數(shù)據(jù)類型,那么就應(yīng)使用類型化存取器方法(如GetInt32 和 GetString),這是因?yàn)樵谧x取列數(shù)據(jù)時(shí),這些方法減少了讀取列數(shù)據(jù)所需的類型轉(zhuǎn)換量。


  • 為避免將不必要的數(shù)據(jù)從服務(wù)器發(fā)送到客戶端,假如你要關(guān)閉閱讀器并拋棄所有保留的結(jié)果,那么在對(duì)閱讀器調(diào)用Close方法前調(diào)用命令對(duì)象的Cancel方法。Cancel方法確保了服務(wù)器的結(jié)果被拋棄,而不會(huì)被發(fā)送到客戶端。相反,對(duì)數(shù)據(jù)閱讀器調(diào)用Close方法會(huì)使閱讀器不必要地提取出保留的結(jié)果,以清空數(shù)據(jù)流。

  • 假如要得到從存儲(chǔ)過(guò)程返回的輸出值或返回值,并且你在利用SqlCommand對(duì)象的ExecuteReader方法,那么在得到輸出或返回值前,必須對(duì)閱讀器調(diào)用Close方法。
  使用XmlReader

  下列情況下,使用通過(guò)調(diào)用SqlCommand對(duì)象的ExecuteXmlReader方法得到的XmlReader對(duì)象:
  • 希望將得到的數(shù)據(jù)作為XML 處理,但不希望引發(fā)因創(chuàng)建DataSet對(duì)象而造成的額外性能開(kāi)銷,并且不需要數(shù)據(jù)的非鏈接緩存。

  • 希望利用SQL Server FOR XML 語(yǔ)法的功能,這種語(yǔ)法答應(yīng)以靈活的方式從數(shù)據(jù)庫(kù)中得到XML片段(即,不帶根元素的XML文檔)。例如,這種方法使你能夠精確指定元素名,是使用元素還是使用以屬性為核心的圖解,圖解是否隨XML數(shù)據(jù)一起被返回,等等。
  更多信息

  假如使用XmlReader,請(qǐng)注重:
  • 在從XmlReader對(duì)象中讀取數(shù)據(jù)時(shí),鏈接必須保持打開(kāi)。SqlCommand對(duì)象的 ExecuteXmlReader方法目前不支持CommandBehavior.CloseConnection枚舉值,因此在使用完閱讀器后必須明確關(guān)閉鏈接。
QQRead.com 推出數(shù)據(jù)恢復(fù)指南教程 數(shù)據(jù)恢復(fù)指南教程 數(shù)據(jù)恢復(fù)故障解析 常用數(shù)據(jù)恢復(fù)方案 硬盤數(shù)據(jù)恢復(fù)教程 數(shù)據(jù)保護(hù)方法 數(shù)據(jù)恢復(fù)軟件 專業(yè)數(shù)據(jù)恢復(fù)服務(wù)指南 獲取單行數(shù)據(jù)

  在這種場(chǎng)景中,將從數(shù)據(jù)源中獲取包含一組指定列的單行數(shù)據(jù)。例如,你得到一個(gè)客戶ID,并希望查找與客戶相關(guān)的細(xì)節(jié);或得到一個(gè)產(chǎn)品ID,并希望得到產(chǎn)品信息。

  方法比較

  假如要對(duì)從數(shù)據(jù)源中得到的一行數(shù)據(jù)執(zhí)行綁定操作,可以用SqlDataAdapter對(duì)象填充DataSet 或DataTable對(duì)象,其方式與在先前討論過(guò)的獲取多行數(shù)據(jù)及重復(fù)場(chǎng)景中描述的方式相同。然而,除非非凡需要DataSet 或DataTable對(duì)象的功能,否則應(yīng)當(dāng)避免創(chuàng)建這些對(duì)象。

  假如需要獲取單行數(shù)據(jù),那么請(qǐng)使用下面的一種方法:
  • 使用存儲(chǔ)過(guò)程輸出參數(shù).

  • 使用SqlDataReader對(duì)象.
  這兩種方法都避免了在服務(wù)器端創(chuàng)建結(jié)果集,在客戶端創(chuàng)建DataSet對(duì)象的不必要額外開(kāi)銷。每種方法的相對(duì)性能要依靠于強(qiáng)度等級(jí)及數(shù)據(jù)庫(kù)鏈接池化是否被使能。當(dāng)數(shù)據(jù)庫(kù)鏈接池化使能時(shí),性能測(cè)試表明存儲(chǔ)過(guò)程方法在高強(qiáng)度環(huán)境下(同時(shí)存在200多鏈接)其性能比SqlDataReader方法高近30%。

  使用存儲(chǔ)過(guò)程輸出參數(shù)

  如下情況中使用存儲(chǔ)過(guò)程輸出參數(shù):
  • 要從鏈接池化使能的多層Web應(yīng)用程序中獲得一行數(shù)據(jù)。
  使用SqlDataReader對(duì)象

  下列情況,需使用SqlDataReader對(duì)象:
  • 除了數(shù)據(jù)值,還需要元數(shù)據(jù)時(shí)。可以利用數(shù)據(jù)閱讀器的GetSchemaTable方法獲取列元數(shù)據(jù)。

  • 未使用鏈接池化時(shí)。在鏈接池化無(wú)效時(shí),SqlDataReader對(duì)象在所有強(qiáng)度環(huán)境下都是好方式;性能測(cè)試表明,在200瀏覽器鏈接時(shí),此方法比存儲(chǔ)過(guò)程方法在性能上要高約20%。
  更多信息
  • 假如知道查詢結(jié)果只需返回一行,那么在調(diào)用SqlCommand對(duì)象的ExecuteReader 方法時(shí),使用CommandBehavior.SingleRow枚舉值。一些供給器,如OLE DB .NET數(shù)據(jù)供給器,用此技巧來(lái)優(yōu)化性能。例如,供給器使用IRow接口(假如此接口存在)而不是代價(jià)更高的IRowset接口。這個(gè)參數(shù)對(duì)SQL Server .NET數(shù)據(jù)供給器沒(méi)有影響。

  • 在使用SqlDataReader對(duì)象時(shí),總是應(yīng)當(dāng)通過(guò)SqlDataReader對(duì)象的類型化存取器方法,如GetString 和GetDecimal,獲得輸出參數(shù)。這樣做就避免了不必要的類型轉(zhuǎn)換。
  獲取單項(xiàng)數(shù)據(jù)

  在本場(chǎng)景中,要獲取單項(xiàng)數(shù)據(jù)。例如,提供了產(chǎn)品ID后,希望查詢單一的產(chǎn)品名;或,給出了客戶名后,希望查詢客戶的信用等級(jí)。在這種場(chǎng)景中,為得到單項(xiàng)數(shù)據(jù),通常不希望引發(fā)創(chuàng)建DataSet 對(duì)象或甚至是 DataTable對(duì)象的額外開(kāi)銷。

  也許只希望檢查數(shù)據(jù)庫(kù)中是否存在特定的行。例如,當(dāng)新用戶在網(wǎng)站注冊(cè)時(shí),需要檢查所選用戶名是否已經(jīng)存在。這是單項(xiàng)數(shù)據(jù)查詢中很非凡的例子,但在此例子中,返回一個(gè)簡(jiǎn)單的布爾返回值就足夠了。

  方法比較

  當(dāng)從數(shù)據(jù)源獲取單項(xiàng)數(shù)據(jù)時(shí),考慮下面的方法:
  • 同存儲(chǔ)過(guò)程一起使用SqlCommand對(duì)象的ExecuteScalar方法。

  • 使用存儲(chǔ)過(guò)程輸出或返回參數(shù)。

  • 使用SqlDataReader對(duì)象。
  ExecuteScalar方法直接返回?cái)?shù)據(jù)項(xiàng),因?yàn)樗菫橹环祷貑蝹€(gè)值的查詢?cè)O(shè)計(jì)的,與存儲(chǔ)過(guò)程輸出參數(shù)和SqlDataReader方法相比,它需要更少的代碼。

  從性能方面來(lái)說(shuō),應(yīng)當(dāng)使用存儲(chǔ)過(guò)程輸出或返回參數(shù),因?yàn)闇y(cè)試結(jié)果表明,存儲(chǔ)過(guò)程方法在從低強(qiáng)度到高強(qiáng)度環(huán)境中(從同時(shí)不到100瀏覽器鏈接到200瀏覽器鏈接)提供了一致的性能。

  更多信息
  • 假如通過(guò)ExecuteQuery方法所執(zhí)行的查詢返回多列和/或行,那么此方法只返回第一行的第一列。
  通過(guò)防火墻建立鏈接

  需要經(jīng)常配置互聯(lián)網(wǎng)應(yīng)用程序以使它能夠通過(guò)防火墻鏈接到SQL Server。例如,許多Web應(yīng)用程序及防火墻的主要結(jié)構(gòu)組件是周邊網(wǎng)絡(luò)(也被稱為DMZ或非軍事化區(qū)),它們用于隔離高端Web服務(wù)器與內(nèi)部網(wǎng)絡(luò)。


  通過(guò)防火墻鏈接到SQL Server時(shí),需要對(duì)防火墻,客戶和服務(wù)器進(jìn)行明確配置。SQL Server提供了客戶網(wǎng)絡(luò)應(yīng)用程序和服務(wù)器網(wǎng)絡(luò)應(yīng)用程序以幫助進(jìn)行配置。

  選擇網(wǎng)絡(luò)庫(kù)

  當(dāng)通過(guò)防火墻建立鏈接時(shí),使用SQL Server TCP/IP網(wǎng)絡(luò)庫(kù)來(lái)簡(jiǎn)化配置,這是SQL Server2000安裝的默認(rèn)選項(xiàng)。假如使用先前版本的SQL Server,那么分別利用客戶端網(wǎng)絡(luò)應(yīng)用程序和服務(wù)器端網(wǎng)絡(luò)應(yīng)用程序檢查TCP/IP是否在客戶和服務(wù)器端已經(jīng)被配置為默認(rèn)的網(wǎng)絡(luò)庫(kù)。

  除了配置優(yōu)點(diǎn),使用TCP/IP庫(kù)還意味著:
  • 受益于大宗數(shù)據(jù)的改進(jìn)性能和增加的擴(kuò)展性。

  • 避免與指定管道相關(guān)的附加安全信息。
  必須在客戶和服務(wù)器計(jì)算機(jī)上配置TCP/IP,因?yàn)榇蠖鄶?shù)防火墻限制了流量通過(guò)的端口,所以必須仔細(xì)考慮SQL Server所使用的端口號(hào)。

  配置服務(wù)器

  SQL Server的默認(rèn)實(shí)例監(jiān)聽(tīng)1433端口。然而,SQL Server 2000的指定實(shí)例在它們首次開(kāi)啟時(shí),動(dòng)態(tài)地分配端口號(hào)。網(wǎng)絡(luò)治理員有希望在防火墻打開(kāi)一定范圍的端口;因此,當(dāng)隨防火墻使用SQL Server的指定實(shí)例時(shí),利用服務(wù)網(wǎng)絡(luò)應(yīng)用程序?qū)?shí)例進(jìn)行配置,使它監(jiān)聽(tīng)特定的端口。然后治理員對(duì)防火墻進(jìn)行配置,以使防火墻答應(yīng)流量到達(dá)特定的IP地址及服務(wù)器實(shí)例所監(jiān)聽(tīng)的端口。

  注重,客戶端網(wǎng)絡(luò)庫(kù)所使用的源端口號(hào)在1024-5000間動(dòng)態(tài)分配。這是TCP/IP客戶端應(yīng)用程序的標(biāo)準(zhǔn)作法,但這意味著防火墻必須答應(yīng)途經(jīng)此范圍的任何端口流量能夠通過(guò)。關(guān)于SQL Server所使用的端口的更多信息,在微軟產(chǎn)品支持服務(wù)網(wǎng)站上,參見(jiàn)INF: P 通過(guò)防火墻對(duì)SQL Server進(jìn)行通訊所需的TCP端口。 QQread.com 推出各大專業(yè)服務(wù)器評(píng)測(cè) Linux服務(wù)器的安全性能 SUN服務(wù)器 HP服務(wù)器 DELL服務(wù)器 IBM服務(wù)器 聯(lián)想服務(wù)器 浪潮服務(wù)器 曙光服務(wù)器 同方服務(wù)器 華碩服務(wù)器 寶德服務(wù)器 動(dòng)態(tài)查找指定實(shí)例

  假如改變了SQL Server所監(jiān)聽(tīng)的默認(rèn)端口,那么就要對(duì)客戶端進(jìn)行配置,以使它鏈接到此端口。更多細(xì)節(jié),見(jiàn)本文中的配置客戶端一節(jié)。

  假如改變了SQL Server 2000默認(rèn)實(shí)例的端口號(hào),那么不修改客戶端將導(dǎo)致鏈接錯(cuò)誤。假如存在多個(gè)SQL Server 實(shí)例,最新版本的MDAC數(shù)據(jù)訪問(wèn)堆棧(2.6)將進(jìn)行動(dòng)態(tài)查找,并利用用戶數(shù)據(jù)報(bào)協(xié)議(UDP)協(xié)商(通過(guò)UDP端口1434)對(duì)指定實(shí)例進(jìn)行定位。盡管這種方法在開(kāi)發(fā)環(huán)境下也許有效,但在現(xiàn)在環(huán)境中卻不大可能正常工作,因?yàn)榈湫桶l(fā)問(wèn)下防火墻阻止UDP協(xié)商流量的通過(guò)。

  為了避開(kāi)這種情況,總是將客戶端配置為鏈接到已配置好的目的端口號(hào)。

  配置客戶端

  應(yīng)當(dāng)對(duì)客戶端進(jìn)行配置以利用TCP/IP網(wǎng)絡(luò)庫(kù)鏈接到SQL Server,并且也應(yīng)當(dāng)確保客戶端庫(kù)使用了正確的目的端口號(hào)。

  使用TCP/IP 網(wǎng)絡(luò)庫(kù)

  利用SQL Server客戶端網(wǎng)絡(luò)庫(kù),可以對(duì)客戶端進(jìn)行配置。在某些安裝版本中,可能沒(méi)有將這個(gè)應(yīng)用程序安裝到客戶端(如Web服務(wù)器)。在這種情況下,可以按如下方式之一解決:
  • 利用通過(guò)鏈接字符串提供的“Network Library=dbmssocn”名稱-值對(duì)指定網(wǎng)絡(luò)庫(kù)。字符串dbmssocn用于標(biāo)識(shí)TCP/IP(套接字)庫(kù)。
  注重 在使用SQL Server .NET數(shù)據(jù)供給器時(shí),網(wǎng)絡(luò)庫(kù)的默認(rèn)設(shè)置是使用“dbmssocn”。
  • 在客戶端機(jī)器上修改注冊(cè)表,把TCP/IP設(shè)置為默認(rèn)庫(kù)。
  指定端口

  假如SQL Server的實(shí)例被配置為監(jiān)聽(tīng)默認(rèn)的1433以外的其它端口,那么通過(guò)以下操作,就能指定鏈接到的端口號(hào):
  • 使用客戶端網(wǎng)絡(luò)應(yīng)用程序

  • 利用提供給鏈接字符串的“Server”或“Data Source”名稱-值對(duì)來(lái)指定端口號(hào)。要按下面的格式使用字符串:

  • "Data Source=ServerName,PortNumber"
  注重 ServerName可以是IP地址,或域名系統(tǒng)(DNS)名,為了優(yōu)化性能,可以使用IP 地址以避免DNS 查詢。

  分布式事務(wù)處理

  假如開(kāi)發(fā)了使用COM+分布式事務(wù)處理和微軟分布式事務(wù)處理協(xié)調(diào)器(DTC)服務(wù)的服務(wù)組件,那么就需要對(duì)防火墻進(jìn)行配置,以答應(yīng)DTC流在不同DTC實(shí)例間及DTC與資源治理器(例如SQL Server)間流動(dòng)。

  處理BLOBs

  目前,很多應(yīng)用程序除了處理許多傳統(tǒng)的字符串和數(shù)字型數(shù)據(jù)外,還要處理象圖形或聲音--甚至復(fù)雜的數(shù)據(jù)格式,如視頻格式的數(shù)據(jù)。圖形、聲音與視頻的數(shù)據(jù)格式類型不一。然而從存儲(chǔ)角度來(lái)說(shuō),它們都可被視為二進(jìn)制數(shù)據(jù)塊,通常將其稱為BLOBs(二進(jìn)制大對(duì)象)。

  SQL Server提供了binary, varbinary, 和image數(shù)據(jù)格式來(lái)存儲(chǔ)BLOBs。不考慮名稱,BLOB數(shù)據(jù)也可被稱為基于文件的數(shù)據(jù)。例如,你可能要存儲(chǔ)與特定行相關(guān)的二進(jìn)制長(zhǎng)注釋字段。SQL Server為此目的提供了ntext 和text數(shù)據(jù)類型。

  通常,對(duì)于小于8KB的二進(jìn)制數(shù)據(jù),使用varbinary數(shù)據(jù)類型。對(duì)于超過(guò)此大小的二進(jìn)制數(shù)據(jù),使用image 。表2 匯集了每個(gè)數(shù)據(jù)類型的主要特性。

  表2 數(shù)據(jù)類型特性

數(shù)據(jù)類型 大小 描述 binary 范圍從1-8KB。存儲(chǔ)大小是指定大小加4字節(jié)。固定長(zhǎng)度的二進(jìn)制數(shù)據(jù) varbinary 范圍從1-8KB。存儲(chǔ)大小是所提供數(shù)據(jù)的實(shí)際大小加4字節(jié)。可變長(zhǎng)度的二進(jìn)制數(shù)據(jù) image 從0-2GB大小的可變長(zhǎng)度二進(jìn)制數(shù)據(jù)大容量可變長(zhǎng)度二進(jìn)制數(shù)據(jù) text 從0-2GB大小的可變長(zhǎng)度數(shù)據(jù)字符型數(shù)據(jù) ntext 從0-2GB大小的可變長(zhǎng)度數(shù)據(jù)寬字節(jié)字符數(shù)據(jù)
  何處存儲(chǔ)BLOB數(shù)據(jù)

  SQL Server 7.0及其以后版本已經(jīng)提高了存儲(chǔ)在數(shù)據(jù)庫(kù)中的BLOB數(shù)據(jù)的使用性能。這種情況的一個(gè)原因是數(shù)據(jù)庫(kù)頁(yè)面大小已經(jīng)增加到了8KB。結(jié)果,小于8KB的文本或圖象數(shù)據(jù)不必再存儲(chǔ)在頁(yè)面單獨(dú)的二進(jìn)制樹結(jié)構(gòu)中,而是能被存儲(chǔ)在單行中。這意味著讀取和寫入text, ntext, 或 image數(shù)據(jù)能象讀取或?qū)懭胱址蚨M(jìn)制字符串那樣快。超出8KB后,將在行中建立一個(gè)指針,數(shù)據(jù)本身存儲(chǔ)在獨(dú)立數(shù)據(jù)頁(yè)面的二進(jìn)制樹結(jié)構(gòu)中,這不可避免會(huì)對(duì)性能產(chǎn)生沖擊。


  關(guān)于迫使text, ntext, 和 image數(shù)據(jù)存儲(chǔ)在單行中的更多信息,見(jiàn)SQL Server在線圖書中的使用text和image數(shù)據(jù)主題。

  一個(gè)經(jīng)常使用的處理BLOB數(shù)據(jù)的可選方法是,將BLOB數(shù)據(jù)存儲(chǔ)在文件系統(tǒng)中,并在數(shù)據(jù)庫(kù)列中存儲(chǔ)一個(gè)指針(通常是一個(gè)統(tǒng)一資源定位器--URL鏈接)以引用正確的文件。對(duì)于SQL Server 7.0以前的版本,將BLOB數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù)外的文件系統(tǒng)中,可以提高性能。

  然而,SQL Server 2000改進(jìn)了BLOB支持,以及ADO.NET對(duì)讀取和寫入BLOB數(shù)據(jù)的支持,使在數(shù)據(jù)庫(kù)中存儲(chǔ)BLOB數(shù)據(jù)成為一種可行的方法。

  在數(shù)據(jù)庫(kù)中存儲(chǔ)BLOB 數(shù)據(jù)的優(yōu)點(diǎn)

  將BLOB數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù)中,帶來(lái)了很多優(yōu)點(diǎn):
  • 易于保持BLOB數(shù)據(jù)與行中其它項(xiàng)數(shù)據(jù)的同步。

  • BLOB數(shù)據(jù)由數(shù)據(jù)庫(kù)所支持,擁有單一的存儲(chǔ)流,易于治理。

  • 通過(guò)SQL Server 2000所支持的XML可以訪問(wèn)BLOB數(shù)據(jù),這將在XML流中返回64位編碼描述的數(shù)據(jù)。

  • 對(duì)包含了固定或可變長(zhǎng)度的字符(包括寬字符)數(shù)據(jù)的列可以執(zhí)行SQL Server全文本搜索(FTS)操作。也可以對(duì)包含在image字段中的已格式化的基于文本的數(shù)據(jù)--WordExcel文檔--執(zhí)行FTS操作。
  將BLOB數(shù)據(jù)寫入到數(shù)據(jù)庫(kù)中

  下面的代碼演示了如何利用ADO.NET將從某個(gè)文件獲得的二進(jìn)制數(shù)據(jù)寫入SQL Server image字段中。

public void StorePicture( string filename )
{
 // Read the file into a byte array
 FileStream fs = new FileStream( filename, FileMode.Open, FileAccess.Read );
 byte[] imageData = new Byte[fs.Length];
 fs.Read( imageData, 0, (int)fs.Length );
 fs.Close();
 SqlConnection conn = new SqlConnection("");
 SqlCommand cmd = new SqlCommand("StorePicture", conn);
 cmd.CommandType = CommandType.StoredProcedure;
 cmd.Parameters.Add("@filename", filename );
 cmd.Parameters["@filename"].Direction = ParameterDirection.Input;
 cmd.Parameters.Add("@blobdata", SqlDbType.Image);
 cmd.Parameters["@blobdata"].Direction = ParameterDirection.Input;
 // Store the byte array within the image field
 cmd.Parameters["@blobdata"].Value = imageData;
 try
 {
  conn.Open();
  cmd.ExecuteNonQuery();
 }
 catch
 {
  throw;
 }
 finally
 {
  conn.Close();
 }
}


  從數(shù)據(jù)庫(kù)中讀取BLOB數(shù)據(jù)

  在通過(guò)ExecuteReader方法創(chuàng)建SqlDataReader對(duì)象以讀取包含BLOB數(shù)據(jù)的行時(shí),需使用CommandBehavior.SequentialAccess枚舉值。假如沒(méi)有此枚舉值,閱讀器一次只從服務(wù)器中向客戶端發(fā)送一行數(shù)據(jù)。假如行包含了BOLB數(shù)據(jù),這預(yù)示著要占用大量?jī)?nèi)存。通過(guò)利用枚舉值,就獲得了更好的控制權(quán),因?yàn)锽LOB數(shù)據(jù)只在被引用時(shí)才被發(fā)出(例如,利用GetBytes方法,可以控制讀取的字節(jié)數(shù))。這在下面的代碼片段中進(jìn)行了演示。

// Assume previously established command and connection
// The command SELECTs the IMAGE column from the table
conn.Open();
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
reader.Read();
// Get size of image data - pass null as the byte array parameter
long bytesize = reader.GetBytes(0, 0, null, 0, 0);
// Allocate byte array to hold image data
byte[] imageData = new byte[bytesize];
long bytesread = 0;
int curpos = 0;
while (bytesread < bytesize)
{
 // chunkSize is an arbitrary application defined value
 bytesread += reader.GetBytes(0, curpos, imageData, curpos, chunkSize);
 curpos += chunkSize;
}
// byte array 'imageData' now contains BLOB from database
  注重使用CommandBehavior.SequentialAccess需要以嚴(yán)格的順序訪問(wèn)列數(shù)據(jù)。例如,假如BLOB數(shù)據(jù)存在于第3列,并且還需要從第1,2列中讀取數(shù)據(jù),那么在讀取第3列前必須先讀取第1,2列。

  事務(wù)處理

  實(shí)際上所有用于更新數(shù)據(jù)源的面向商業(yè)的應(yīng)用程序都需要事務(wù)處理支持。通過(guò)提供四個(gè)基本擔(dān)保,即眾所周知的首字縮寫ACID:可分性,一致性,分離性,和耐久性,事務(wù)處理將用于確保包含在一個(gè)或多個(gè)數(shù)據(jù)源中的系統(tǒng)的完整性。

  例如,考慮一個(gè)基于Web的零售應(yīng)用程序,它用于處理購(gòu)買訂單。每個(gè)訂單需要3個(gè)完全不同操作,這些操作涉及到3個(gè)數(shù)據(jù)庫(kù)更新:
  • 庫(kù)存水準(zhǔn)必須減少所訂購(gòu)的數(shù)量。

  • 所購(gòu)買的量必須記入客戶的信用等級(jí)。

  • 新訂單必須增加到數(shù)據(jù)庫(kù)中。
  這三個(gè)不同的操作作為一個(gè)單元并自動(dòng)執(zhí)行是至關(guān)重要的。三個(gè)操作必須全部成功,或都不成功--任何一個(gè)操作出現(xiàn)誤差都將破壞數(shù)據(jù)完整性。事務(wù)處理提供了這種完整性及其它保證。

  可以采用很多方法將事務(wù)治理合并到數(shù)據(jù)訪問(wèn)代碼中。每種方法適合下面兩種基本編程模型之一。
  • 手工事務(wù)處理。可以直接在組件代碼或存儲(chǔ)過(guò)程中分別編寫利用ADO.NET 或 Transact-SQL事務(wù)處理支持特性的代碼。

  • 自動(dòng)化(COM+)事務(wù)處理。可以向.NET類中增加聲明在運(yùn)行時(shí)指定對(duì)象事務(wù)處理需要的屬性。這種模型使你能方便地配置多個(gè)組件以使它們?cè)谕皇聞?wù)處理內(nèi)運(yùn)行。
  盡管自動(dòng)化事務(wù)處理模型極大地簡(jiǎn)化了分布式事務(wù)處理過(guò)程,但兩種模型都用于執(zhí)行本地事務(wù)處理(即對(duì)單個(gè)資源治理器如SQL Server 2000執(zhí)行的事務(wù)處理)或分布式事務(wù)處理(即,對(duì)位于遠(yuǎn)程計(jì)算機(jī)上的多個(gè)資源治理執(zhí)行的事務(wù)處理)。

  你也許會(huì)試圖利用自動(dòng)化(COM+)事務(wù)處理來(lái)從易于編程的模型中獲益。在有多個(gè)組件執(zhí)行數(shù)據(jù)庫(kù)更新的系統(tǒng)中,這種優(yōu)點(diǎn)更明顯。然而,在很多情況下,應(yīng)當(dāng)避免這種事務(wù)處理模型所帶來(lái)的額外開(kāi)銷和性能損失。

  本節(jié)將指導(dǎo)你根據(jù)特定的應(yīng)用程序環(huán)境選擇最合適的模型。

  選擇事務(wù)處理模型

  在選擇事務(wù)處理模型前,首先應(yīng)當(dāng)考慮是否真正需要事務(wù)處理。事務(wù)處理是服務(wù)器應(yīng)用程序使用的最昂貴的資源,在不必要使用的地方,它們降低了擴(kuò)展性。考慮下面用于治理事務(wù)處理使用的準(zhǔn)則:
  • 只在需要跨一組操作獲取鎖并需要加強(qiáng)ACID規(guī)則時(shí)才執(zhí)行事務(wù)處理。

  • 盡可能短地保持事務(wù)處理,以最小化維持?jǐn)?shù)據(jù)庫(kù)鎖的時(shí)間。

  • 永遠(yuǎn)不要將客戶放到事務(wù)處理生命周期的控制之中。

  • 不要為單個(gè)SQL語(yǔ)句使用事務(wù)處理。SQL Server自動(dòng)把每個(gè)語(yǔ)句作為單個(gè)事務(wù)處理執(zhí)行。
  自動(dòng)化事務(wù)處理與手工事務(wù)處理的對(duì)比

  盡管編程模型已經(jīng)對(duì)自動(dòng)化事務(wù)處理進(jìn)行了簡(jiǎn)化,非凡是在多個(gè)組件執(zhí)行數(shù)據(jù)庫(kù)更新時(shí),但本地事務(wù)處理總是相當(dāng)快,因?yàn)樗鼈儾恍枰c微軟DTC交互。即使你對(duì)單個(gè)本地資源治理器(如SQL Server)使用自動(dòng)化事務(wù)處理,也是這種情況(盡管性能損失減少了),因?yàn)槭质奖镜厥聞?wù)處理避免了所有不必要的與DTC的進(jìn)程間通信。

  對(duì)于下面的情況,需使用手工事務(wù)處理:
  • 對(duì)單個(gè)數(shù)據(jù)庫(kù)執(zhí)行事務(wù)處理。
  對(duì)于下列情況,則宜使用自動(dòng)事務(wù)處理:
  • 需要將單個(gè)事務(wù)處理擴(kuò)展到多個(gè)遠(yuǎn)程數(shù)據(jù)庫(kù)時(shí)。

  • 需要單個(gè)事務(wù)處理?yè)碛卸鄠€(gè)資源治理器(如數(shù)據(jù)庫(kù)和Windows 2000消息隊(duì)列(被稱為MSMQ)資源治理器)時(shí)。
  注重 避免混用事務(wù)處理模型。最好只使用其中一個(gè)。

  在性能足夠好的應(yīng)用程序環(huán)境中,(甚至對(duì)于單個(gè)數(shù)據(jù)庫(kù))選擇自動(dòng)化事務(wù)處理以簡(jiǎn)化編
程模型,這種做法是合理的。自動(dòng)化事務(wù)處理使多個(gè)組件能很輕易地執(zhí)行現(xiàn)一事務(wù)處理中的多個(gè)操作。

  使用手工事務(wù)處理

  對(duì)于手工事務(wù)處理,可以直接在組件代碼或存儲(chǔ)過(guò)程中分別編寫使用ADO.NET 或 Transact-SQL事務(wù)處理支持特性的代碼。多數(shù)情況下,應(yīng)選擇在存儲(chǔ)過(guò)程中控制事務(wù)處理,因?yàn)檫@種方法提供了更高的封裝性,并且在性能方面,此方法與利用ADO.NET 代碼執(zhí)行事務(wù)處理兼容。

  利用ADO.NET執(zhí)行手工事務(wù)處理

  ADO.NET支持事務(wù)處理對(duì)象,利用此對(duì)象可以開(kāi)始新事務(wù)處理過(guò)程,并明確控制事務(wù)處理是否執(zhí)行還是回滾。事務(wù)處理對(duì)象與單個(gè)數(shù)據(jù)庫(kù)鏈接相關(guān),可以通過(guò)鏈接對(duì)象的BeginTransaction方法獲得。調(diào)用此方法并不是暗示,接下來(lái)的命令是在事務(wù)處理上下文中發(fā)出的。必須通過(guò)設(shè)置命令的Transaction屬性,明確地將每個(gè)命令與事務(wù)處理關(guān)聯(lián)起來(lái)。可以將多個(gè)命令對(duì)象與事務(wù)處理對(duì)象關(guān)聯(lián),因此在單個(gè)事務(wù)處理中就針對(duì)單個(gè)數(shù)據(jù)庫(kù)把多個(gè)操作進(jìn)行分組。

  更多信息
  • ADO.NET手工事務(wù)處理的默認(rèn)分離級(jí)別是讀聯(lián)鎖,這意味著在讀取數(shù)據(jù)時(shí),數(shù)據(jù)庫(kù)控制共享鎖,但在事務(wù)處理結(jié)束前,數(shù)據(jù)可以被修改。這種情況潛在地會(huì)產(chǎn)生不可重復(fù)的讀取或虛數(shù)據(jù)。通過(guò)將事務(wù)處理對(duì)象的IsolationLevel屬性設(shè)置為IsolationLevel枚舉類型所定義的一個(gè)枚舉值,就可改變分離級(jí)別。

  • 必須仔細(xì)為事務(wù)處理選擇合適的分離級(jí)別。其折衷是數(shù)據(jù)一致性與性能的比例。最高的分離等級(jí)(被序列化了)提供了絕對(duì)的數(shù)據(jù)一致性,但是以系統(tǒng)整體吞吐量為代價(jià)。較低的分離等級(jí)會(huì)使應(yīng)用程序更易于擴(kuò)展,但同時(shí)增加了因數(shù)據(jù)不一致而導(dǎo)致出錯(cuò)的可能性。對(duì)多數(shù)時(shí)間讀取數(shù)據(jù)、極少寫入數(shù)據(jù)的系統(tǒng)來(lái)說(shuō),較低的分離等級(jí)是合適的。

  • 關(guān)于選擇恰當(dāng)事務(wù)處理級(jí)別極有價(jià)值的信息,見(jiàn)微軟出版社名為Inside SQL Server 2000的書,作者Kalen Delaney。
  利用存儲(chǔ)過(guò)程執(zhí)行手工事務(wù)處理

  也可以在存儲(chǔ)過(guò)程中使用Transact-SQL語(yǔ)句直接控制手工事務(wù)處理。例如,可以利用包含了Transact-SQL事務(wù)處理語(yǔ)句(如BEGIN TRANSACTION、END TRANSACTION及ROLLBACK TRANSACTION)的存儲(chǔ)過(guò)程執(zhí)行事務(wù)處理。

  更多信息
  • 假如需要,可以在存儲(chǔ)過(guò)程中使用SET TRANSACTION ISOLATION LEVEL語(yǔ)句控制事務(wù)處理的分離等級(jí)。讀聯(lián)鎖是SQL Server的默認(rèn)設(shè)置。關(guān)于SQL Server分離級(jí)別的更多信息,見(jiàn)SQL Server在線書目“訪問(wèn)和修改關(guān)系數(shù)據(jù)”一節(jié)中的分離級(jí)別部分。
  使用自動(dòng)化事務(wù)


  自動(dòng)化事務(wù)簡(jiǎn)化了編程模型,因?yàn)樗鼈儾恍枰鞔_地開(kāi)始新事務(wù)處理過(guò)程,或明確執(zhí)行或取消事務(wù)。然而,自動(dòng)化事務(wù)的最大優(yōu)點(diǎn)是它們能與DTC結(jié)合起來(lái),這就使單個(gè)事務(wù)可以擴(kuò)展到多個(gè)分布式數(shù)據(jù)源中。在大型分布式應(yīng)用程序中,這個(gè)優(yōu)點(diǎn)是很重要的。盡管通過(guò)手工對(duì)DTC直接編程來(lái)控制分布式事務(wù)是可能的,但自動(dòng)化事務(wù)處理極大的簡(jiǎn)化了工作量,并且它是為基于組件的系統(tǒng)而設(shè)計(jì)的。例如,可以方便地以說(shuō)明方式配置多個(gè)組件以執(zhí)行包含了單個(gè)事務(wù)處理的任務(wù)。

  自動(dòng)化事務(wù)依靠于COM+提供的分布式事務(wù)處理支持特性。結(jié)果,只有服務(wù)組件(即從ServicedComponent類中派生的組件)能夠使用自動(dòng)化事務(wù)。

  要為自動(dòng)化事務(wù)處理配置類,操作如下:
  • 從位于EnterpriseServices名稱空間的ServicedComponent類中派生新類。

  • 通過(guò)Transaction屬性定義類的事務(wù)處理需求。來(lái)自TransactionOption的枚舉值決定了如何在COM+類中配置類。可與此屬性一同設(shè)置的其它屬性包括事務(wù)處理分離等級(jí)和超時(shí)上限。

  • 為了避免必須明確選出事務(wù)處理結(jié)果,可以用AutoComplete屬性對(duì)方法進(jìn)行注釋。假如這些方法釋放異常,事務(wù)將自動(dòng)取消。注重,假如需要,仍可以直接挑選事務(wù)處理結(jié)果。
  更多信息
  • 關(guān)于COM+自動(dòng)化事務(wù)的更多信息,可在平臺(tái)SDK文檔中搜索“通過(guò)COM+的自動(dòng)化事務(wù)”獲取。

  • 關(guān)于.NE T事務(wù)處理類的示例,見(jiàn)附錄中的如何編碼.NET事務(wù)處理。
配置事務(wù)處理分離級(jí)別
 
  用于COM+1.0版--即運(yùn)行在Windows 2000中的COM+--的事務(wù)處理分離級(jí)別被序列化了。這樣做提供了最高的分離等級(jí),卻是以性能為代價(jià)的。系統(tǒng)的整體吞吐量被降低了。因?yàn)樗婕暗降馁Y源治理器(典型地是數(shù)據(jù)庫(kù))在事務(wù)處理期間必須保持讀和寫鎖。在此期間,其它所有事務(wù)處理都被阻斷了,這種情況將對(duì)應(yīng)用程序的擴(kuò)展能力產(chǎn)生極大沖擊。

  隨微軟Windows .NET發(fā)行的COM+ 1.5版答應(yīng)有COM+目錄中按組件配置事務(wù)處理分離等級(jí)。與事務(wù)中根組件相關(guān)的設(shè)置決定了事務(wù)處理的分離等級(jí)。另外,同一事務(wù)流中的內(nèi)部子組件擁有的事務(wù)處理等級(jí)必須不能高于要組件所定義的等級(jí)。假如不是這樣,當(dāng)子組件實(shí)例化時(shí),將導(dǎo)致錯(cuò)誤。

  對(duì).NET治理類,Transaction屬性支持所有的公有Isolation屬性。你可以用此屬性陳述式地指定一非凡分離等級(jí),如下面的代碼所示:

[Transaction(TransactionOption.Supported, Isolation=TransactionIsolationLevel.ReadCommitted)]
public class Account : ServicedComponent
{
. . .
}
  確定事務(wù)處理結(jié)果

  在單個(gè)事務(wù)流的所有事務(wù)處理組件上下文中,自動(dòng)化事務(wù)處理結(jié)果由事務(wù)取消標(biāo)志和一致性標(biāo)志的狀態(tài)決定。當(dāng)事務(wù)流中的根組件成為非活動(dòng)狀態(tài)(并且控制權(quán)返回調(diào)用者)時(shí),確定事務(wù)處理結(jié)果。這種情況在圖5中得到了演示,此圖顯示的是一個(gè)典型的銀行基金傳送事務(wù)。 

  當(dāng)根對(duì)象(在本例中是對(duì)象)變?yōu)榉腔顒?dòng)狀態(tài),并且客戶的方法調(diào)用返回時(shí),確定事務(wù)處理結(jié)果。在任何上下文中的任何一致性標(biāo)志被設(shè)為假,或假如事務(wù)處理取消標(biāo)志設(shè)為真,那么底層的物理DTC事務(wù)將被取消。

  可以以下面兩種方式之一從.NET對(duì)象中控制事務(wù)處理結(jié)果:
  • 可以用AutoComplete屬性對(duì)方法進(jìn)行注釋,并讓.NET自動(dòng)存放將決定事務(wù)處理結(jié)果投票。假如方法釋放異常,利用此屬性,一致性標(biāo)志自動(dòng)地被設(shè)為假(此值最終使事務(wù)取消)。假如方法返回而沒(méi)有釋放異常,那么一致性標(biāo)志將設(shè)為真,此值指出組件樂(lè)于執(zhí)行事務(wù)。這并沒(méi)有得到保證,因?yàn)樗揽坑谕皇聞?wù)流中其它對(duì)象的投票。

  • 可以調(diào)用ContextUtil類的靜態(tài)方法SetComplete或 SetAbort,這些方法分別將一致性標(biāo)志設(shè)為真或假。
  嚴(yán)重性大于10的SQL Server錯(cuò)誤將導(dǎo)致治理數(shù)據(jù)供給器釋放SqlException類型的異常。假如方法緩存并處理異常,就要確保或者通過(guò)手工取消了事務(wù),或者方法被標(biāo)記了[AutoComplete],以保證異常能傳遞回調(diào)用者。

  AutoComplete方法

  對(duì)于標(biāo)記了屬性的方法,執(zhí)行下面操作:
  • 將SqlException傳遞加調(diào)用堆棧。

  • 將SqlException封裝在外部例外中,并傳遞回調(diào)用者。也可以將異常封裝在對(duì)調(diào)用者更有意義的異常類型中。
  異常假如不能傳遞,將導(dǎo)致對(duì)象不會(huì)提出取消事務(wù),從而忽視數(shù)據(jù)庫(kù)錯(cuò)誤。這意味著共享同一事務(wù)流的其它對(duì)象的成功操作將被提交。

  下面的代碼緩存了SqlException,然后將它直接傳遞回調(diào)用者。事務(wù)處理最終將被取消,因?yàn)閷?duì)象的一致性標(biāo)志在對(duì)象變?yōu)榉腔顒?dòng)狀態(tài)時(shí)自動(dòng)被設(shè)為假。

[AutoComplete]
void SomeMethod()
{
try
{
// Open the connection, and perform database Operation
. . .
}
catch (SqlException sqlex )
{
LogException( sqlex ); // Log the exception details
throw; // Rethrow the exception, causing the consistent
// flag to be set to false.
}
finally
{
// Close the database connection
. . .
}
}
  Non-AutoComlete方法

  對(duì)于沒(méi)有AutoComplete的屬性的方法,必須:
  • 在catch塊內(nèi)調(diào)用ContextUtil.SetAbort以終止事務(wù)處理。這就將相容標(biāo)志設(shè)置為假。

  • 假如沒(méi)有發(fā)生異常事件,調(diào)用ContextUtil.SetComplete,以提交事務(wù),這就將相容標(biāo)志設(shè)置為真(缺省狀態(tài))。
  代碼說(shuō)明了這種方法。

void SomeOtherMethod()
{
 try

 {
  // Open the connection, and perform database operation
  . . .
  ContextUtil.SetComplete(); // Manually vote to commit the transaction
 }
 catch (SqlException sqlex)
 {
  LogException( sqlex ); // Log the exception details
  ContextUtil.SetAbort(); // Manually vote to abort the transaction
  // Exception is handled at this point and is not propagated to the caller
 }
 finally
 {
  // Close the database connection
  . . .
 }
}
  注重 假如有多個(gè)catch塊,在方法開(kāi)始的時(shí)候調(diào)用ContextVtil.SetAbort,以及在try塊的末尾調(diào)用ContextUtil.SetComplete都會(huì)變得輕易。用這種方法,就不需要在每個(gè)catch塊中重復(fù)調(diào)用ContextUtil.SetAbort。通過(guò)這種方法確定的相容標(biāo)志的設(shè)置只在方法返回時(shí)有效。

  對(duì)于異常事件(或循環(huán)異常),必須把它傳遞到調(diào)用堆棧中,因?yàn)檫@使得調(diào)用代碼認(rèn)為事務(wù)處理失敗。它答應(yīng)調(diào)用代碼做出優(yōu)化選擇。比如,在銀行資金轉(zhuǎn)賬中,假如債務(wù)操作失敗,則轉(zhuǎn)帳分支可以決定不執(zhí)行債務(wù)操作。

  假如把相容標(biāo)志設(shè)置為假并且在返回時(shí)沒(méi)有出現(xiàn)異常事件,則調(diào)用代碼就沒(méi)有辦法知道事務(wù)處理是否一定失敗。雖然可以返回Boolean值或設(shè)置Boolean輸出參數(shù),但還是應(yīng)該前后一致,通過(guò)顯示異常事件以表明有錯(cuò)誤發(fā)生。這樣代碼就有一種標(biāo)準(zhǔn)的錯(cuò)誤處理方法,因此更簡(jiǎn)明、更具有相容性。數(shù)據(jù)分頁(yè)

  在分布式應(yīng)用程序中利用數(shù)據(jù)進(jìn)行分頁(yè)是一項(xiàng)普遍的要求。比如,用戶可能得到書的列表而該列表又不能夠一次完全顯示,用戶就需要在數(shù)據(jù)上執(zhí)行一些熟悉的操作,比如瀏覽下一頁(yè)或上一頁(yè)的數(shù)據(jù),或者跳到列表的第一頁(yè)或最后一頁(yè)。

  這部分內(nèi)容將討論實(shí)現(xiàn)這種功能的選項(xiàng),以及每種選項(xiàng)在性能和縮放性上的效果。

  選項(xiàng)比較

  數(shù)據(jù)分頁(yè)的選項(xiàng)有:
  • 利用SqlDataAdapter的Fill方法,將來(lái)自查詢處的結(jié)果填充到DataSet中。

  • 通過(guò)COM的可相互操作性使用ADO,并利用服務(wù)器光標(biāo)。

  • 利用存儲(chǔ)的過(guò)程手工實(shí)現(xiàn)數(shù)據(jù)分頁(yè)。
  對(duì)數(shù)據(jù)進(jìn)行分頁(yè)的最優(yōu)選項(xiàng)依靠于下列因素:
  • 擴(kuò)展性要求

  • 性能要求

  • 網(wǎng)絡(luò)帶寬

  • 數(shù)據(jù)庫(kù)服務(wù)器的存儲(chǔ)器和功率

  • 中級(jí)服務(wù)器的存儲(chǔ)器和功率

  • 由分頁(yè)查詢所返回的行數(shù)

  • 數(shù)據(jù)總頁(yè)數(shù)的大小
  性能測(cè)試表明利用存儲(chǔ)過(guò)程的手工方法在很大的應(yīng)力水平范圍上都提供了最佳性能。然而,由于手工方法在服務(wù)器上執(zhí)行工作,假如大部分站點(diǎn)功能都依靠數(shù)據(jù)分頁(yè)功能,那么服務(wù)器性能就會(huì)成一個(gè)要害要素。為確保這種方法能適合非凡環(huán)境,應(yīng)該測(cè)試各種非凡要求的選項(xiàng)。

  下面將討論各種不同的選項(xiàng)。

  使用SqlDataAdapter

  如前面所討論的,SqlDataAdapter是用來(lái)把來(lái)自數(shù)據(jù)庫(kù)的數(shù)據(jù)填充到DataSet中,過(guò)載的Fill方法中的任一個(gè)都需要兩個(gè)整數(shù)索引值(如下列代碼所示):

public int Fill(
DataSet dataSet,
int startRecord,
int maxRecords,
string srcTable
);
  StartRecord值標(biāo)示從零開(kāi)始的記錄起始索引值。MaxRecord值表示從startRecord開(kāi)始的記錄數(shù),并將拷貝到新的DataSet中。

  SqlDataAdapter在內(nèi)部利用SqlDataReader執(zhí)行查詢并返回結(jié)果。SqlDataAdapter讀取結(jié)果并創(chuàng)建基于來(lái)自SalDataReader的數(shù)據(jù)的Dataset。SqlDataAdapter通過(guò)startRecord和maxRecords把所有結(jié)果都拷貝到新生成的DataSet中,并丟棄不需要的數(shù)據(jù)。這意味著許多不必要的數(shù)據(jù)將潛在的通過(guò)網(wǎng)絡(luò)進(jìn)入數(shù)據(jù)訪問(wèn)客戶--這是這種方法的主要缺陷。

  比如,假如有1000個(gè)記錄,而需要的是第900到950個(gè)記錄,那么前面的899個(gè)記錄將仍然穿越網(wǎng)絡(luò)然后被丟棄。對(duì)于小數(shù)量的記錄,這種開(kāi)銷可能是比較小的,但假如針對(duì)大量數(shù)據(jù)的分頁(yè),則這種開(kāi)銷就會(huì)非常巨大。

  使用ADO

  實(shí)現(xiàn)分頁(yè)的另一個(gè)選項(xiàng)是利用基于COM的ADO進(jìn)行分頁(yè)。這種方法的目標(biāo)是獲得訪問(wèn)服務(wù)器光標(biāo)。服務(wù)器光標(biāo)通過(guò)ADO Recordset對(duì)象顯示。可以把Recordset光標(biāo)的位置設(shè)置到adUseServer中。假如你的OLE DB供給器支持這種設(shè)置(如SQLOLEDB那樣),就可以使用服務(wù)器光標(biāo)。這樣就可以利用光標(biāo)直接導(dǎo)航到起始記錄,而不需要將所有數(shù)據(jù)傳過(guò)網(wǎng)絡(luò)進(jìn)入訪問(wèn)數(shù)據(jù)的用戶代碼中。

  這種方法有下面兩個(gè)缺點(diǎn):
  • 在大多數(shù)情況下,可能需要將返回到Recordset對(duì)象中的記錄翻譯成DataSet中的內(nèi)容,以便在客戶治理的代碼中使用。雖然OleDbDataAdapter確實(shí)在獲取ADO Recordset對(duì)象并把它翻譯成Dataset時(shí)過(guò)載了Fill方法,但是并沒(méi)有利用非凡記錄進(jìn)行開(kāi)始與結(jié)束操作的功能。唯一現(xiàn)實(shí)的選項(xiàng)是把開(kāi)始記錄移動(dòng)到Recordset對(duì)象中,循環(huán)每個(gè)記錄,然后手工拷貝數(shù)據(jù)到手工生成的新Dataset中。這種操作,尤其是利用COM Interop調(diào)用,其優(yōu)點(diǎn)可能不僅僅是不需要在網(wǎng)絡(luò)上傳輸多余的數(shù)據(jù),尤其對(duì)于小的DataSet更明顯。

  • 從服務(wù)器輸出所需數(shù)據(jù)時(shí),將保持連接和服務(wù)器光標(biāo)開(kāi)放。在數(shù)據(jù)庫(kù)服務(wù)器上,光標(biāo)的開(kāi)放與維護(hù)需要昂貴的資源。雖然該選項(xiàng)提高了性能,但是由于為延長(zhǎng)的時(shí)間兩消耗服務(wù)器資源,從而也有可能降低可擴(kuò)展性。
  提供手工實(shí)現(xiàn)

  在本部分中討論的數(shù)據(jù)分頁(yè)的最后一個(gè)選項(xiàng)是利用存儲(chǔ)過(guò)程手工實(shí)現(xiàn)應(yīng)用程序的分頁(yè)功能。對(duì)于包含唯一要害字的表格,實(shí)現(xiàn)存儲(chǔ)過(guò)程相對(duì)輕易一些。而對(duì)于沒(méi)有唯一要害字的表格(也不應(yīng)該有許多要害字),該過(guò)程會(huì)相對(duì)復(fù)雜一些。


  帶有唯一要害字的表格的分頁(yè)

  假如表格包含一個(gè)唯一要害字,就可以利用WHERE條款中的要害字創(chuàng)建從某個(gè)非凡行起始的結(jié)果設(shè)置。這種方法,與用來(lái)限制結(jié)果設(shè)置大小的SET ROWCOUNT狀態(tài)是相匹配的,提供了一種有效的分頁(yè)原理。這一方法將在下面存儲(chǔ)的代碼中說(shuō)明:

CREATE PROCEDURE GetProductsPaged
 @lastProductID int,
 @pageSize int
AS
 SET ROWCOUNT @pageSize
 SELECT *
 FROM Products
 WHERE [standard search criteria]
 AND ProductID > @lastProductID
 ORDER BY [Criteria that leaves ProductID monotonically increasing]
GO
  這個(gè)存儲(chǔ)過(guò)程的調(diào)用程序僅僅維護(hù)LastProductID的值,并通過(guò)所選的連續(xù)調(diào)用之間的頁(yè)的大小增加或減小該值。

  不帶有唯一要害字的表格的分頁(yè)

  假如需要分頁(yè)的表格沒(méi)有唯一要害字,可以考慮添加一個(gè)--比如利用標(biāo)識(shí)欄。這樣就可以實(shí)現(xiàn)上面討論的分頁(yè)方案了。

  只要能夠通過(guò)結(jié)合結(jié)果記錄中的兩個(gè)或更多區(qū)域來(lái)產(chǎn)生唯一性,就仍然有可能實(shí)現(xiàn)無(wú)唯一要害字表格的有效分頁(yè)方案。

  比如,考察下列表格:

Col1 Col2 Col3 Other columns… A 1 W … A 1 X   . A 1 Y   . A 1 Z   . A 2 W   . A 2 X   . B 1 W … B 1 X   .
  對(duì)于該表,結(jié)合Col 、Col2 和Col3就可能產(chǎn)生一種唯一性。這樣,就可以利用下面存儲(chǔ)過(guò)程中的方法實(shí)現(xiàn)分布原理:

CREATE PROCEDURE RetrieveDataPaged
 @lastKey char(40),
 @pageSize int
AS
 SET ROWCOUNT @pageSize
 SELECT
  Col1, Col2, Col3, Col4, Col1+Col2+Col3 As KeyField
  FROM SampleTable
  WHERE [Standard search criteria]
  AND Col1+Col2+Col3 > @lastKey
  ORDER BY Col1 ASC, Col2 ASC, Col3 ASC
GO
  客戶保持存儲(chǔ)過(guò)程返回的keyField欄的最后值,然后又插入回到存儲(chǔ)過(guò)程中以控制表的分頁(yè)。

  雖然手工實(shí)現(xiàn)增加了數(shù)據(jù)庫(kù)服務(wù)器上的應(yīng)變,但它避免了在網(wǎng)絡(luò)上傳輸不必要的數(shù)據(jù)。性能測(cè)試表明在整個(gè)應(yīng)變水平中這種方法都工作良好。然而,根據(jù)站點(diǎn)工作所涉及的數(shù)據(jù)分頁(yè)功能的多少,在服務(wù)器上進(jìn)行手工分頁(yè)可能影響應(yīng)用程序的可擴(kuò)展性。應(yīng)該在所在環(huán)境中運(yùn)行性能測(cè)試,為應(yīng)用程序找到最合適的方法。 QQread.com 推出各大專業(yè)服務(wù)器評(píng)測(cè) Linux服務(wù)器的安全性能 SUN服務(wù)器 HP服務(wù)器 DELL服務(wù)器 IBM服務(wù)器 聯(lián)想服務(wù)器 浪潮服務(wù)器 曙光服務(wù)器 同方服務(wù)器 華碩服務(wù)器 寶德服務(wù)器 附錄

  如何為一個(gè).NET類啟用對(duì)象結(jié)構(gòu)

  要利用Enterprise (COM+)Services為對(duì)象結(jié)構(gòu)啟用.NET治理的類,需要執(zhí)行下列步驟:
  • 從位于System. Enterprise Services名字空間中的Serviced Component中導(dǎo)出所需類。

    using System.EnterpriseServices;
    public class DataAccessComponent : ServicedComponent
  • 為該類添加Construction Enabled屬性,并合理地指定缺省結(jié)構(gòu)字符串,該缺省值保存在COM+目錄中,治理員可以利用組件服務(wù)微軟治理控制臺(tái)(MNC)的snap-in來(lái)維護(hù)該缺省值。

    [ConstructionEnabled(Default="default DSN")]
    public class DataAccessComponent : ServicedComponent
  • 提供虛擬Construct方法的替換實(shí)現(xiàn)方案。該方法在對(duì)象語(yǔ)言構(gòu)造程序之后調(diào)用。在COM目錄中保存的結(jié)構(gòu)字符串是該方法的唯一字符串。

    public override void Construct( string constructString )
    {
    // Construct method is called next after constructor.
    // The configured DSN is supplied as the single argument
    }
  • 通過(guò)Assembly key文件或Assembly key Name屬性為該匯編提供一個(gè)強(qiáng)名字。任何用COM+服務(wù)注冊(cè)的匯編必須有一個(gè)強(qiáng)名字。關(guān)于帶有強(qiáng)名字匯編的更多信息,參考:http://msdn.microsoft.com/library/en-us/cpguide/html/cpconworkingwithstrongly- namedassemblies.Asp。
    [assembly: AssemblyKeyFile("DataServices.snk")]

  • 為支持動(dòng)態(tài)注冊(cè),可以利用匯編層上的屬性ApplicationName和Application Action分別指定用于保持匯編元素和應(yīng)用程序動(dòng)作類型的COM+應(yīng)用程序的名字。
// the ApplicationName attribute specifies the name of the
// COM+ Application which will hold assembly components
[assembly : ApplicationName("DataServices")]

// the ApplicationActivation.ActivationOption attribute specifies
// where assembly components are loaded on activation
// Library : components run in the creator's process

// Server : components run in a system process, dllhost.exe
[assembly: ApplicationActivation(ActivationOption.Library)]

      下列代碼段是一個(gè)叫做DataAccessComponent的服務(wù)組件,它利用COM+結(jié)構(gòu)字符串來(lái)獲得數(shù)據(jù)庫(kù)連接字符串。

    using System;
    using System.EnterpriseServices;
    // the ApplicationName attribute specifies the name of the
    // COM+ Application which will hold assembly components
    [assembly : ApplicationName("DataServices")]

    // the ApplicationActivation.ActivationOption attribute specifies
    // where assembly components are loaded on activation
    // Library : components run in the creator's process
    // Server : components run in a system process, dllhost.exe
    [assembly: ApplicationActivation(ActivationOption.Library)]

    // Sign the assembly. The snk key file is created using the
    // sn.exe utility
    [assembly: AssemblyKeyFile("DataServices.snk")]

    [ConstructionEnabled(Default="Default DSN")]
    public class DataAccessComponent : ServicedComponent
    {
    private string connectionString;
    public DataAccessComponent()
    {
    // constructor is called on instance creation
    }
    public override void Construct( string constructString )
    {
    // Construct method is called next after constructor.
    // The configured DSN is supplied as the single argument
    this.connectionString = constructString;
    }
    }

      如何利用SqlDataAdapter來(lái)檢索多個(gè)行

      下面的代碼說(shuō)明如何利用SqlDataAdapter對(duì)象發(fā)出一個(gè)生成Data Set或Datatable的命令。它從SQL Server Northwind數(shù)據(jù)庫(kù)中檢索一系列產(chǎn)品目錄。

    using System.Data;
    using System.Data.SqlClient;
    public DataTable RetrieveRowsWithDataTable()
    {
     using ( SqlConnection conn = new SqlConnection(connectionString) )
     {
      SqlCommand cmd = new SqlCommand("DATRetrieveProducts", conn);
      cmd.CommandType = CommandType.StoredProcedure;
      SqlDataAdapter da = new SqlDataAdapter( cmd );
      DataTable dt = new DataTable("Products");
      da.Fill(dt);
      return dt;
     }
    }

      按下列步驟利用SqlAdapter生成DataSet或DataTable:
    • 創(chuàng)建SqlCommand對(duì)象啟用存儲(chǔ)過(guò)程,并把它與SqlConnection對(duì)象(顯示的)或連接字符串(未顯示)相聯(lián)系。

    • 創(chuàng)建一個(gè)新的SqlDataAdapter對(duì)象,并把它SqlCommand對(duì)象相聯(lián)系。

    • 創(chuàng)建DataTable(或者DataSet)對(duì)象。利用構(gòu)造程序自變量命名DataTable.

    • 調(diào)用SqlData Adapter對(duì)象的Fill方法,把檢索的行轉(zhuǎn)移到DataSet或Datatable中。
      如何利用SqlDataReader檢索多個(gè)行

      下列代碼說(shuō)明了如何利用SqlDataReader方法檢索多行:

    using System.IO;
    using System.Data;
    using System.Data.SqlClient;
    public SqlDataReader RetrieveRowsWithDataReader()
    {
     SqlConnection conn = new SqlConnection("server=(local);
             Integrated Security=SSPI;database=northwind");
     SqlCommand cmd = new SqlCommand("DATRetrieveProducts", conn );
     cmd.CommandType = CommandType.StoredProcedure;
     try
     {
      conn.Open();
      // Generate the reader. CommandBehavior.CloseConnection causes the
      // the connection to be closed when the reader object is closed
      return( cmd.ExecuteReader( CommandBehavior.CloseConnection ) );
     }
     catch
     {
      conn.Close();

      throw;
     }
    }

    // Display the product list using the console
    private void DisplayProducts()
    {
     SqlDataReader reader = RetrieveRowsWithDataReader();
     while (reader.Read())
     {
      Console.WriteLine("{0} {1} {2}",
      reader.GetInt32(0).ToString(),
      reader.GetString(1) );
     }
     reader.Close(); // Also closes the connection due to the
     // CommandBehavior enum used when generating the reader
    }

      按下列步驟利用SqlDataReader檢索多行:
    • 創(chuàng)建用于執(zhí)行存儲(chǔ)的過(guò)程的SqlCommand對(duì)象,并把它與SqlConnection對(duì)象相聯(lián)系。

    • 打開(kāi)鏈接。

    • 通過(guò)調(diào)用SqlCommand對(duì)象的Excute Reader方法生成SqlDataReader對(duì)象。

    • 從流中讀取數(shù)據(jù),調(diào)用SqlDataReader對(duì)象的Read方法來(lái)檢索行,并利用分類的存取程序方法(如GetIut 32和Get String方法)檢索列的值。

    • 完成讀取后,調(diào)用Close方法。
    如何利用XmlReader檢索多個(gè)行

      可以利用SqlCommand對(duì)象生成XmlReader對(duì)象,它提供對(duì)XML數(shù)據(jù)的基于流的前向訪問(wèn)。該命令(通常是一個(gè)存儲(chǔ)的過(guò)程)必須生成一個(gè)基于XML的結(jié)果設(shè)置,它對(duì)于SQL Server2000通常是由帶有有效條款FOR XML的SELECT狀態(tài)組成。下列代碼段說(shuō)明了這種方法:

    public void RetrieveAndDisplayRowsWithXmlReader()
    {
     SqlConnection conn = new SqlConnection(connectionString);
     SqlCommand cmd = new SqlCommand("DATRetrieveProductsXML", conn );
     cmd.CommandType = CommandType.StoredProcedure;
     try
     {
      conn.Open();
      XmlTextReader xreader = (XmlTextReader)cmd.ExecuteXmlReader();
      while ( xreader.Read() )
      {
       if ( xreader.Name == "PRODUCTS" )
       {
        string strOutput = xreader.GetAttribute("ProductID");
        strOutput += " ";
        strOutput += xreader.GetAttribute("ProductName");
        Console.WriteLine( strOutput );
       }
      }
      xreader.Close();
     }
     catch
     {
      throw;
     }
     finally
     {
      conn.Close();
     }
    }
      上述代碼使用了下列存儲(chǔ)過(guò)程:


    CREATE PROCEDURE DATRetrieveProductsXML
    AS
     SELECT * FROM PRODUCTS
     FOR XML AUTO
    GO
      按下列步驟檢索XML數(shù)據(jù):
    • 創(chuàng)建SqlCommand對(duì)象啟用生成XML結(jié)果設(shè)置的過(guò)程。(比如,利用SELECT狀態(tài)中的FOR XML條款)。把SqlCommand對(duì)象與一個(gè)鏈接相聯(lián)系。

    • 調(diào)用SqlCommand對(duì)象的ExecuteXmlReader方法,并把結(jié)果分配給前向?qū)ο骕mlTextReader。當(dāng)不需要任何返回?cái)?shù)據(jù)的基于XML的驗(yàn)證時(shí),這是應(yīng)該使用的最快類型的XmlReader對(duì)象。

    • 利用XmlTextReader對(duì)象的Read方法讀取數(shù)據(jù)。
      如何利用存儲(chǔ)過(guò)程輸出參數(shù)檢索單個(gè)行

      可以調(diào)用一個(gè)存儲(chǔ)過(guò)程,它通過(guò)一種稱做輸出參數(shù)的方式可以在單個(gè)行中返回檢索數(shù)據(jù)項(xiàng)。下列代碼段利用存儲(chǔ)的過(guò)程檢索產(chǎn)品的名稱和單價(jià),該產(chǎn)品包含在Northwind數(shù)據(jù)庫(kù)中。

    void GetProductDetails( int ProductID,
    out string ProductName, out decimal UnitPrice )
    {
     SqlConnection conn = new SqlConnection( "server=(local);
              Integrated Security=SSPI;database=Northwind");
     // Set up the command object used to execute the stored proc
     SqlCommand cmd = new SqlCommand( "DATGetProductDetailsSPOutput", conn );
     cmd.CommandType = CommandType.StoredProcedure;
     // Establish stored proc parameters.
     // @ProductID int INPUT
     // @ProductName nvarchar(40) OUTPUT
     // @UnitPrice money OUTPUT

     // Must explicitly set the direction of output parameters
     SqlParameter paramProdID = cmd.Parameters.Add( "@ProductID", ProductID );
     paramProdID.Direction = ParameterDirection.Input;
     SqlParameter paramProdName = cmd.Parameters.Add( "@ProductName", SqlDbType.VarChar, 40 );

     paramProdName.Direction = ParameterDirection.Output;
     SqlParameter paramUnitPrice = cmd.Parameters.Add( "@UnitPrice", SqlDbType.Money );
     paramUnitPrice.Direction = ParameterDirection.Output;
     try
     {
      conn.Open();
      // Use ExecuteNonQuery to run the command.
      // Although no rows are returned any mapped output parameters
      // (and potentially return values) are populated
      cmd.ExecuteNonQuery( );
      // Return output parameters from stored proc
      ProductName = paramProdName.Value.ToString();
      UnitPrice = (decimal)paramUnitPrice.Value;
     }
     catch
     {
      throw;
     }
     finally
     {
      conn.Close();
     }
    }

      按下列步驟利用存儲(chǔ)的過(guò)程輸出參數(shù)檢索單個(gè)行:
    • 創(chuàng)建一個(gè)SqlCommand對(duì)象,并把它與SqlConnection對(duì)象相聯(lián)系。

    • 通過(guò)調(diào)用SqlCommand’s Parameters集合的Add方法設(shè)置存儲(chǔ)過(guò)程參數(shù)。缺省情況下,參數(shù)假定為輸出參數(shù),所以必須明確設(shè)置任何輸出參數(shù)的方向。
      注重 明確設(shè)置所有參數(shù)的方向是一次很好的練習(xí),包括輸入?yún)?shù)。
    • 打開(kāi)連接。

    • 調(diào)用Sqlcommand對(duì)象的ExecuteNonQuery方法。它在輸出參數(shù)(并潛在地帶有一個(gè)返回值)中。

    • 利用Value屬性從合適的SqlParameter對(duì)象中檢索輸出參數(shù)。

    • 關(guān)閉連接。
      上述代碼段啟用了下列存儲(chǔ)過(guò)程。

    CREATE PROCEDURE DATGetProductDetailsSPOutput
     @ProductID int,
     @ProductName nvarchar(40) OUTPUT,
     @UnitPrice money OUTPUT
    AS
     SELECT @ProductName = ProductName,
        @UnitPrice = UnitPrice
      FROM Products
      WHERE ProductID = @ProductID
    GO
      如何利用SqlDataReader檢索單個(gè)行

      可以利用SqlDataReader對(duì)象檢索單個(gè)行,以及來(lái)自返回?cái)?shù)據(jù)流的所需欄的值。這由下列代碼說(shuō)明:

    void GetProductDetailsUsingReader( int ProductID,
    out string ProductName, out decimal UnitPrice )
    {
     SqlConnection conn = new SqlConnection("server=(local);
            Integrated Security=SSPI;database=Northwind");
     // Set up the command object used to execute the stored proc
     SqlCommand cmd = new SqlCommand( "DATGetProductDetailsReader", conn );
     cmd.CommandType = CommandType.StoredProcedure;
     // Establish stored proc parameters.
     // @ProductID int INPUT

     SqlParameter paramProdID = cmd.Parameters.Add( "@ProductID", ProductID );
     paramProdID.Direction = ParameterDirection.Input;
     try
     {
      conn.Open();
      SqlDataReader reader = cmd.ExecuteReader();
      reader.Read(); // Advance to the one and only row

      // Return output parameters from returned data stream
      ProductName = reader.GetString(0);
      UnitPrice = reader.GetDecimal(1);
      reader.Close();
     }
     catch
     {
      throw;
     }
     finally
     {
      conn.Close();
     }
    }

      按下列步驟返回帶有SqlDataReader對(duì)象:
    • 建立SqlCommand對(duì)象。

    • 打開(kāi)連接。

    • 調(diào)用SqlDReader對(duì)象的ExecuteReader對(duì)象。

    • 利用SqlDataReader對(duì)象的分類的存取程序方法檢索輸出參數(shù)--在這里是GetString和GetDecimal.
      上述代碼段啟用了下列存儲(chǔ)過(guò)程:

    CREATE PROCEDURE DATGetProductDetailsReader
     @ProductID int
    AS
     SELECT ProductName, UnitPrice FROM Products
      WHERE ProductID = @ProductID
    GO
      如何利用ExecuteScalar單個(gè)項(xiàng)

      ExecuteScalar方法是設(shè)計(jì)成用于返回單個(gè)值的訪問(wèn)。在返回多列或多行的訪問(wèn)事件中,ExecuteScalar只返回第一行的第一例。

      下列代碼說(shuō)明如何查詢某個(gè)產(chǎn)品ID的產(chǎn)品名稱:


    void GetProductNameExecuteScalar( int ProductID, out string ProductName )
    {
     SqlConnection conn = new SqlConnection( "server=(local);
            Integrated Security=SSPI;database=northwind");
     SqlCommand cmd = new SqlCommand("LookupProductNameScalar", conn );
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("@ProductID", ProductID );
     try
     {
      conn.Open();
      ProductName = (string)cmd.ExecuteScalar();
     }
     catch
     {
      throw;
     }
     finally
     {
      conn.Close();
     }
    }

      按下列步驟利用Execute Scalar檢索單個(gè)項(xiàng):
    • 建立調(diào)用存儲(chǔ)過(guò)程的SqlCommand對(duì)象。

    • 打開(kāi)鏈接。

    • 調(diào)用ExecuteScalar方法,注重該方法返回對(duì)象類型。它包含檢索的第一列的值,并且必須設(shè)計(jì)成合適的類型。

    • 關(guān)閉鏈接。
      上述代碼啟用了下列存儲(chǔ)過(guò)程:

    CREATE PROCEDURE LookupProductNameScalar
     @ProductID int
    AS
     SELECT TOP 1 ProductName
      FROM Products
      WHERE ProductID = @ProductID
    GO
      如何利用存儲(chǔ)過(guò)程輸出或返回的參數(shù)檢索單個(gè)項(xiàng)

      利用存儲(chǔ)過(guò)程輸出或返回的參數(shù)可以查詢單個(gè)值,下列代碼說(shuō)明了輸出參數(shù)的使用:

    void GetProductNameUsingSPOutput( int ProductID, out string ProductName )
    {
    SqlConnection conn = new SqlConnection( "server=(local);
                Integrated Security=SSPI;
                database=northwind");
    SqlCommand cmd = new SqlCommand("LookupProductNameSPOutput", conn );
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter paramProdID = cmd.Parameters.Add("@ProductID", ProductID );
    ParamProdID.Direction = ParameterDirection.Input;
    SqlParameter paramPN = cmd.Parameters.Add("@ProductName", SqlDbType.VarChar, 40 );
    paramPN.Direction = ParameterDirection.Output;
    try
    {
     conn.Open();
     cmd.ExecuteNonQuery();
     ProductName = paramPN.Value.ToString();
    }
    catch
    {
     throw;
    }
    finally
    {
     conn.Close();
    }
    }

      按下列步驟利用存儲(chǔ)過(guò)程的輸出參數(shù)檢索單個(gè)值:
    • 創(chuàng)建調(diào)用存儲(chǔ)過(guò)程的SqlCommand對(duì)象。

    • 通過(guò)把SqlParmeters添加到SqlCommand’s Parameters集合中設(shè)置任何輸入?yún)?shù)和單個(gè)輸出參數(shù)。

    • 打開(kāi)鏈接。

    • 調(diào)用SqlCommand對(duì)象的Execute NonQuery方法。

    • 關(guān)閉鏈接。

    • 利用輸出SqlParameter的Value屬性檢索輸出值。
      上述代碼使用了下列存儲(chǔ)過(guò)程:

    CREATE PROCEDURE LookupProductNameSPOutput
     @ProductID int,
     @ProductName nvarchar(40) OUTPUT
    AS
     SELECT @ProductName = ProductName
     FROM Products
     WHERE ProductID = @ProductID
    GO
      下列代碼說(shuō)明如何利用返回值確定是否存在非凡行。從編碼的角度看,這與使用存儲(chǔ)過(guò)程輸出參數(shù)相類似,除了需要明確設(shè)置到ParameterDirection.ReturnValue的SqlParameter方向。

    bool CheckProduct( int ProductID )
    {
     SqlConnection conn = new SqlConnection( "server=(local);
                 Integrated Security=SSPI;database=northwind");
     SqlCommand cmd = new SqlCommand("CheckProductSP", conn );
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("@ProductID", ProductID );
     SqlParameter paramRet = cmd.Parameters.Add("@ProductExists", SqlDbType.Int );
     paramRet.Direction = ParameterDirection.ReturnValue;
     try
     {
      conn.Open();
      cmd.ExecuteNonQuery();
     }
     catch
     {
      throw;
     }
     finally

     {
      conn.Close();
     }
     return (int)paramRet.Value == 1;
    }

      按下列步驟,可以利用存儲(chǔ)過(guò)程返回值檢查是否存在非凡行:
    • 建立調(diào)用存儲(chǔ)過(guò)程的SqlCommand對(duì)象。

    • 設(shè)置包含需要訪問(wèn)的行的主要要害字的輸入?yún)?shù)。

    • 設(shè)置單個(gè)返回值參數(shù)。把SqlParameter對(duì)象添加到SqlCommand’s Parameter集合中,并設(shè)置它到ParameterDireetion.ReturnValue的方面。

    • 打開(kāi)鏈接。

    • 調(diào)用SqlCommand對(duì)象的ExecuteNonQuery的方法.

    • 關(guān)閉鏈接。

    • 利用返回值SqlParameter的Value屬性檢索返回值。
      上述代碼使用了下列存儲(chǔ)過(guò)程:

    CREATE PROCEDURE CheckProductSP
     @ProductID int
    AS
     IF EXISTS( SELECT ProductID FROM Products
          WHERE ProductID = @ProductID )
       return 1
     ELSE
      return 0
    GO
        如何利用SqlDataReader檢索單個(gè)項(xiàng)。
     
      通過(guò)調(diào)用命令對(duì)象的ExecuteReader方法,可以利用SqlDataReader對(duì)象獲得單個(gè)輸出值。這需要稍微多一些的代碼,因?yàn)镾qlDataReader Read方法必須調(diào)用,然后所需值通過(guò)讀者存取程序方法得到檢索。SqlDataReader對(duì)象的使用在下列代碼中說(shuō)明:

    bool CheckProductWithReader( int ProductID )
    {
     SqlConnection conn = new SqlConnection( "server=(local);
                  Integrated Security=SSPI;database=northwind");
     SqlCommand cmd = new SqlCommand("CheckProductExistsWithCount", conn );
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("@ProductID", ProductID );
     cmd.Parameters["@ProductID"].Direction = ParameterDirection.Input;
     try
     {
      conn.Open();
      SqlDataReader reader = cmd.ExecuteReader( CommandBehavior.SingleResult );
      reader.Read();

      bool bRecordExists = reader.GetInt32(0) > 0;
      reader.Close();
      return bRecordExists;
     }
     catch
     {
      throw;
     }
     finally
     {
      conn.Close();
     }

    }

      上述代碼使用了下列存儲(chǔ)過(guò)程:

    CREATE PROCEDURE CheckProductExistsWithCount
     @ProductID int
    AS
     SELECT COUNT(*) FROM Products
       WHERE ProductID = @ProductID
    GO QQread.com 推出各大專業(yè)服務(wù)器評(píng)測(cè) Linux服務(wù)器的安全性能 SUN服務(wù)器 HP服務(wù)器 DELL服務(wù)器 IBM服務(wù)器 聯(lián)想服務(wù)器 浪潮服務(wù)器 曙光服務(wù)器 同方服務(wù)器 華碩服務(wù)器 寶德服務(wù)器 如何編碼ADO.NET手工事務(wù)

      下列代碼說(shuō)明如何利用SQL Server. NET數(shù)據(jù)供給器提供的事務(wù)支持來(lái)保護(hù)事務(wù)的支金轉(zhuǎn)帳操作。該操作在位于同一數(shù)據(jù)庫(kù)中的兩個(gè)帳戶之間轉(zhuǎn)移支金。

    public void TransferMoney( string toAccount, string fromAccount, decimal amount )
    {
     using ( SqlConnection conn = new SqlConnection(
          "server=(local);Integrated Security=SSPI;database=SimpleBank" ) )
     {
      SqlCommand cmdCredit = new SqlCommand("Credit", conn );
      cmdCredit.CommandType = CommandType.StoredProcedure;
      cmdCredit.Parameters.Add( new SqlParameter("@AccountNo", toAccount) );
      cmdCredit.Parameters.Add( new SqlParameter("@Amount", amount ));
      SqlCommand cmdDebit = new SqlCommand("Debit", conn );
      cmdDebit.CommandType = CommandType.StoredProcedure;
      cmdDebit.Parameters.Add( new SqlParameter("@AccountNo", fromAccount) );
      cmdDebit.Parameters.Add( new SqlParameter("@Amount", amount ));

      conn.Open();
      // Start a new transaction
      using ( SqlTransaction trans = conn.BeginTransaction() )
      {
       // Associate the two command objects with the same transaction
       cmdCredit.Transaction = trans;
       cmdDebit.Transaction = trans;

      try
      {
       cmdCredit.ExecuteNonQuery();
       cmdDebit.ExecuteNonQuery();
       // Both commands (credit and debit) were successful
       trans.Commit();
      }
      catch( Exception ex )
      {
       // transaction failed
       trans.Rollback();
       // log exception details . . .
       throw ex;
      }
     }
     }
    }

      如何利用Transact-SQL執(zhí)行事務(wù)

      下列存儲(chǔ)過(guò)程說(shuō)明了如何在Transact-SQL過(guò)程內(nèi)執(zhí)行事務(wù)的支金轉(zhuǎn)移操作。

    CREATE PROCEDURE MoneyTransfer
     @FromAccount char(20),
     @ToAccount char(20),
     @Amount money
    AS

    BEGIN TRANSACTION
     -- PERFORM DEBIT OPERATION
     UPDATE Accounts
     SET Balance = Balance - @Amount
     WHERE AccountNumber = @FromAccount
     IF @@RowCount = 0
     BEGIN
      RAISERROR('Invalid From Account Number', 11, 1)
      GOTO ABORT
     END

    DECLARE @Balance money
     SELECT @Balance = Balance FROM ACCOUNTS
      WHERE AccountNumber = @FromAccount
      IF @BALANCE < 0
      BEGIN
       RAISERROR('Insufficient funds', 11, 1)
       GOTO ABORT
      END
      -- PERFORM CREDIT OPERATION
      UPDATE Accounts
       SET Balance = Balance + @Amount
        WHERE AccountNumber = @ToAccount
        IF @@RowCount = 0
        BEGIN
         RAISERROR('Invalid To Account Number', 11, 1)
         GOTO ABORT
        END
      COMMIT TRANSACTION
      RETURN 0
      ABORT:
       ROLLBACK TRANSACTION
     GO
      該存儲(chǔ)過(guò)程使用BEGIN TRANSACTION, COMMIT TRANSACTION,和ROLLBACK TRANSACTION狀態(tài)手工控制事務(wù)。

      如何編碼事務(wù)性的.NET類

      下述例子是三種服務(wù)性的NET類,它們配置或用于自動(dòng)事務(wù)。每個(gè)類都帶有Transaction屬性,它的值將決定是否啟動(dòng)新事務(wù)流或者對(duì)象是否共享即時(shí)調(diào)用程序的數(shù)據(jù)流。這些元素一起工作來(lái)執(zhí)行銀行支金轉(zhuǎn)移。Transfer類配置有RequiresNew事務(wù)屬性,而Debit和Credit類配置有Required屬性。這樣,在運(yùn)行的時(shí)候三個(gè)對(duì)象共享同一個(gè)事務(wù)。

    using System;
    using System.EnterpriseServices;
    [Transaction(TransactionOption.RequiresNew)]
    public class Transfer : ServicedComponent
    {
     [AutoComplete]
     public void Transfer( string toAccount,
     string fromAccount, decimal amount )
     {
      try
      {
       // Perform the debit operation
       Debit debit = new Debit();
       debit.DebitAccount( fromAccount, amount );
       // Perform the credit operation
       Credit credit = new Credit();
       credit.CreditAccount( toAccount, amount );
      }
      catch( SqlException sqlex )
      {
       // Handle and log exception details
       // Wrap and propagate the exception
       throw new TransferException( "Transfer Failure", sqlex );
      }
     }
    }

    [Transaction(TransactionOption.Required)]
    public class Credit : ServicedComponent
    {
     [AutoComplete]
     public void CreditAccount( string account, decimal amount )
     {
      SqlConnection conn = new SqlConnection(
        "Server=(local); Integrated Security=SSPI"; database="SimpleBank");
      SqlCommand cmd = new SqlCommand("Credit", conn );
      cmd.CommandType = CommandType.StoredProcedure;
      cmd.Parameters.Add( new SqlParameter("@AccountNo", account) );
      cmd.Parameters.Add( new SqlParameter("@Amount", amount ));
      try
      {
       conn.Open();
       cmd.ExecuteNonQuery();

      }
      catch (SqlException sqlex)
      {
       // Log exception details here
       throw; // Propagate exception
      }
     }
    }

    [Transaction(TransactionOption.Required)]
    public class Debit : ServicedComponent
    {
     public void DebitAccount( string account, decimal amount )
     {
      SqlConnection conn = new SqlConnection(
       "Server=(local); Integrated Security=SSPI"; database="SimpleBank");
      SqlCommand cmd = new SqlCommand("Debit", conn );
      cmd.CommandType = CommandType.StoredProcedure;
      cmd.Parameters.Add( new SqlParameter("@AccountNo", account) );
      cmd.Parameters.Add( new SqlParameter("@Amount", amount ));
      try
      {
       conn.Open();
       cmd.ExecuteNonQuery();
      }
      catch (SqlException sqlex)
      {
       // Log exception details here
       throw; // Propagate exception back to caller
      }
     }
    }


    發(fā)表評(píng)論 共有條評(píng)論
    用戶名: 密碼:
    驗(yàn)證碼: 匿名發(fā)表
    主站蜘蛛池模板: 蒲城县| 关岭| 永兴县| 安吉县| 鹿泉市| 贵港市| 木里| 汝州市| 弥勒县| 凭祥市| 山东| 宜宾市| 祁连县| 菏泽市| 永昌县| 怀来县| 丰县| 临清市| 贺州市| 分宜县| 武冈市| 平果县| 兰坪| 信丰县| 兴化市| 奎屯市| 五华县| 东台市| 盐津县| 溧阳市| 长治市| 凤冈县| 元氏县| 霍林郭勒市| 景谷| 策勒县| 来凤县| 林西县| 长武县| 台山市| 成安县|