摘要:學(xué)習(xí)如何使用 SQL Server 2000 和 Visual Studio .NET 2003 為 DotNetKB ASP.NET 解決方案創(chuàng)建數(shù)據(jù)存儲(chǔ)層。此外,還討論了有關(guān) SQL Server、IIS 和 ASP.NET 的安全性問(wèn)題。
簡(jiǎn)介
在《ASP.NET應(yīng)用程序規(guī)劃與設(shè)計(jì)》中,我們討論了名為 DotNetKB 的 ASP.NET 解決方案的基礎(chǔ)規(guī)劃和設(shè)計(jì)知識(shí)。本部分將詳細(xì)討論如何使用 Microsoft? SQL Server? 2000 和 Microsoft Visual Studio? .NET 2003 創(chuàng)建數(shù)據(jù)存儲(chǔ)層。其中包括創(chuàng)建數(shù)據(jù)庫(kù)(添加表、索引、約束條件和關(guān)系)以及編寫(xiě)用于存取數(shù)據(jù)的存儲(chǔ)過(guò)程。同時(shí),我們還將討論與 SQL Server、Internet 信息服務(wù)器 (IIS) 和 ASP.NET 有關(guān)的安全性問(wèn)題。到本部分結(jié)束時(shí),我們將獲得一個(gè)適用于 DotNetKB 解決方案的功能完備且安全的數(shù)據(jù)存儲(chǔ)系統(tǒng)。
使用 Visual Studio .NET 2003 創(chuàng)建數(shù)據(jù)庫(kù)圖
Visual Studio .NET 2003 的眾多優(yōu)勢(shì)之一是,用戶(hù)可將其用作主要的 SQL Server 編輯器來(lái)完成大多數(shù)任務(wù)。獲得目標(biāo)數(shù)據(jù)庫(kù)服務(wù)器的適當(dāng)權(quán)限后,您就可以輕松地使用 Visual Studio .NET 2003 創(chuàng)建各種數(shù)據(jù)庫(kù)、表、索引、約束條件、關(guān)系、視圖、存儲(chǔ)過(guò)程和功能了。Visual Studio .NET 提供了一個(gè)可供您完成上述操作的默認(rèn)數(shù)據(jù)庫(kù)項(xiàng)目,還包括了用于創(chuàng)建表、觸發(fā)器、存儲(chǔ)過(guò)程等的大量模板,非常便于使用。最后,因?yàn)槭褂?Visual Studio .NET 作為 SQL Server 的編輯環(huán)境,所以還可以使用它將所有 SQL Server 腳本存儲(chǔ)到 Microsoft Visual SourceSafe? 中。這對(duì)于共享項(xiàng)目和其他需要長(zhǎng)期維護(hù)的情況而言非常方便。
引用用戶(hù)方案
針對(duì)本系列文章中的項(xiàng)目 DotNetKB,我創(chuàng)建了 30 多個(gè)用戶(hù)方案,用于標(biāo)識(shí)支持本系列文章第 1 部分所概括的應(yīng)用程序所需的任務(wù)。我們將使用這些用戶(hù)方案來(lái)標(biāo)識(shí)表中存儲(chǔ)的數(shù)據(jù)以及為管理這些數(shù)據(jù)而在運(yùn)行時(shí)執(zhí)行的存儲(chǔ)過(guò)程。下面是部分用戶(hù)方案列表。完整的列表可以從相關(guān)支持站點(diǎn) User Scenarios for DotNetKB Project(英文)上找到。
1、查看按關(guān)鍵字搜索的問(wèn)題列表(按日期倒序排列)
2、查看按日期排序的問(wèn)題列表(按日期倒序排列)
3、查看按主題排序的問(wèn)題列表(按主題的字母順序/問(wèn)題的日期倒序排列)
4、查看某個(gè)特定主題的問(wèn)題列表(按日期倒序排列)
5、查看無(wú)任何解答的問(wèn)題列表(按日期倒序排列)
6、查看問(wèn)題計(jì)數(shù)
7、查看無(wú)解答的問(wèn)題計(jì)數(shù)
8、查看某個(gè)特定主題的問(wèn)題計(jì)數(shù)
9、查看由某位專(zhuān)家解答的問(wèn)題計(jì)數(shù)
10、查看某個(gè)問(wèn)題及其解答列表(按解答日期倒序排列)
11、添加新問(wèn)題
12、編輯現(xiàn)有問(wèn)題
13、刪除現(xiàn)有問(wèn)題及其相關(guān)解答
如您所見(jiàn),列表中僅僅涉及到問(wèn)題記錄的任務(wù)就有許多。而且您還需要處理解答、主題和專(zhuān)家記錄。而在實(shí)際工作中,這才剛剛開(kāi)始。首先,您必須標(biāo)識(shí)需要為每條記錄存儲(chǔ)的數(shù)據(jù)元素(問(wèn)題、解答、主題和專(zhuān)家),還需要將結(jié)果以表格的形式組織到 SQL Server 中的數(shù)據(jù)庫(kù)中。
創(chuàng)建數(shù)據(jù)庫(kù)項(xiàng)目
首先要打開(kāi) Visual Studio .NET 2003 并創(chuàng)建一個(gè)新的數(shù)據(jù)庫(kù)項(xiàng)目。Visual Studio .NET 數(shù)據(jù)庫(kù)項(xiàng)目的類(lèi)型目前還不太確定,因?yàn)殚_(kāi)始新項(xiàng)目時(shí)它隱藏在選項(xiàng)列表中。但開(kāi)始使用后,我想您會(huì)發(fā)現(xiàn)數(shù)據(jù)庫(kù)項(xiàng)目類(lèi)型有許多優(yōu)點(diǎn),所以非常值得花費(fèi)精力去掌握它們。
要使用 Visual Studio .NET 創(chuàng)建一個(gè)新的數(shù)據(jù)庫(kù)項(xiàng)目,需要完成以下任務(wù):
啟動(dòng) Visual Studio .NET,如果新建項(xiàng)目對(duì)話框沒(méi)有自動(dòng)出現(xiàn),請(qǐng)從主菜單中選擇 File(文件)-> New(新建)-> Project(項(xiàng)目)。
當(dāng)顯示 New Project(新建項(xiàng)目)對(duì)話框時(shí),展開(kāi)左側(cè)樹(shù)視圖列表中的 Other Projects(其他項(xiàng)目)文件夾,然后單擊 Database Projects(數(shù)據(jù)庫(kù)項(xiàng)目)文件夾。此時(shí)右側(cè)將顯示 Database Project(數(shù)據(jù)庫(kù)項(xiàng)目)模板。
現(xiàn)在,在 Name:(名稱(chēng):)輸入框中鍵入項(xiàng)目名稱(chēng)。在我的例子中,鍵入的是 DotNetKB_Database,不過(guò)您可以根據(jù)需要鍵入任何內(nèi)容。
然后單擊 OK(確定)按鈕,創(chuàng)建項(xiàng)目并在 Visual Studio .NET 中打開(kāi)它。
屏幕上將出現(xiàn)一個(gè)對(duì)話框,要求您選擇要與該項(xiàng)目相關(guān)聯(lián)的數(shù)據(jù)庫(kù)。此時(shí),先單擊 Cancel(取消)。下一步將創(chuàng)建一個(gè)新數(shù)據(jù)庫(kù)并將其添加到您的項(xiàng)目中。
圖 1 所示為您創(chuàng)建項(xiàng)目時(shí),該項(xiàng)目在 Visual Studio .NET 中的外觀。

圖 1:創(chuàng)建一個(gè)新的數(shù)據(jù)庫(kù)項(xiàng)目
創(chuàng)建要使用的新數(shù)據(jù)庫(kù)之后,可以在該數(shù)據(jù)庫(kù)與您的項(xiàng)目之間建立一個(gè)連接,以便在 Visual Studio .NET 2003 中對(duì)其進(jìn)行操作。為此,需要完成以下任務(wù):
在 Solution Explorer(解決方案資源管理器)窗口中,展開(kāi)您的 dotNETKB_Database 項(xiàng)目,以顯示 Database References(數(shù)據(jù)庫(kù)引用)項(xiàng)。
在 Database References(數(shù)據(jù)庫(kù)引用)項(xiàng)上單擊鼠標(biāo)右鍵,然后從上下文相關(guān)菜單中選擇 New Database Reference...(新建數(shù)據(jù)庫(kù)引用...),打開(kāi) Data Link Properties(數(shù)據(jù)鏈接屬性)對(duì)話框。
輸入您在其中添加 DotNetKB 數(shù)據(jù)庫(kù)的數(shù)據(jù)庫(kù)服務(wù)器的名稱(chēng),然后輸入您的登錄憑據(jù)并從下拉菜單中選擇 DotNetKB。
單擊 OK(確定)按鈕,將引用添加到您的項(xiàng)目中。
圖 2 所示為完成上述操作時(shí)對(duì)話框的外觀。

圖 2:Data Link Properties(數(shù)據(jù)鏈接屬性)對(duì)話框
至此,數(shù)據(jù)庫(kù)創(chuàng)建完畢并被添加為您項(xiàng)目的引用。下一步,定義存儲(chǔ) DotNetKB 解決方案數(shù)據(jù)所需的表。
使用 Visual Studio .NET 定義數(shù)據(jù)庫(kù)表
在 Visual Studio .NET 中定義數(shù)據(jù)庫(kù)表的最簡(jiǎn)單的方法是創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)圖。這樣,您可以在一個(gè)類(lèi)似“所見(jiàn)即所得”的編輯器中定義所有細(xì)節(jié)。您只需展開(kāi) Server Explorer(服務(wù)器資源管理器)中相應(yīng)的樹(shù),在 Database Diagrams(數(shù)據(jù)庫(kù)圖)項(xiàng)上單擊鼠標(biāo)右鍵,然后從上下文相關(guān)菜單中選擇 New Database Diagram...(新建數(shù)據(jù)庫(kù)圖...),即可啟動(dòng)一個(gè)新的空白圖。第一次啟動(dòng)某個(gè)圖時(shí),系統(tǒng)將要求您從數(shù)據(jù)庫(kù)中選擇一個(gè)表。因?yàn)槟形磩?chuàng)建任何表,所以可以忽略該對(duì)話框。現(xiàn)在,可以開(kāi)始定義您的表了。
在生成的《ASP.NET應(yīng)用程序規(guī)劃與設(shè)計(jì)》用戶(hù)方案文檔包含定義表所需的信息。學(xué)習(xí)定義如何在系統(tǒng)中添加新記錄的方案,通常是了解需要存儲(chǔ)哪些數(shù)據(jù)的最佳途徑。有時(shí),您需要查看諸如記錄更新甚至是報(bào)告之類(lèi)的其他方案,以確保沒(méi)有遺漏其他字段。在本示例中,“添加記錄”方案就是一個(gè)很好的參考方案。
例如,以下是用于添加問(wèn)題的方案:
添加新問(wèn)題
向系統(tǒng)中添加一條新問(wèn)題記錄,其中包括標(biāo)題、日期/時(shí)間、指明該問(wèn)題所屬類(lèi)別的主題 ID 以及問(wèn)題正文。有時(shí)還需要提供問(wèn)題提出者姓名及其電子郵件地址。添加新問(wèn)題之后,將向調(diào)用函數(shù)返回一個(gè)唯一的整數(shù)問(wèn)題 ID。
重要名詞以粗體表示。閱讀方案說(shuō)明時(shí),這些名詞或表名(例如,“問(wèn)題記錄”就是一個(gè)很好的例子)往往能夠表明需要存儲(chǔ)哪些數(shù)據(jù)。使用上述信息,您可以在數(shù)據(jù)庫(kù)圖中添加一個(gè)新表并定義所需的列。下面的示例詳細(xì)介紹了如何在數(shù)據(jù)庫(kù)圖中添加表。
在圖“surface”上單擊鼠標(biāo)右鍵并從上下文相關(guān)菜單中選擇 New Table...(新建表...)。輸入 Questions(問(wèn)題)作為表名,然后單擊 OK(確定)將其添加到圖中。
在 Questions(問(wèn)題)表對(duì)話框中,鍵入上文所述方案中提供的字段信息。例如,Column Name(列名)= ID、Data Type(數(shù)據(jù)類(lèi)型)= int、Length(長(zhǎng)度)= 4,并取消選擇 Allow Nulls(允許為空)復(fù)選框。對(duì)該表的其余部分重復(fù)上述操作(參見(jiàn)圖 3)。

圖 3:Questions(問(wèn)題)表
您將看到,第一列 (ID) 旁邊有一個(gè)小的金色鍵。它表示該字段是該表的主鍵字段。要設(shè)置主鍵字段,可以在列表中的列名上單擊鼠標(biāo)右鍵,然后從上下文相關(guān)菜單中選擇 Primary Key(主鍵)。另外,還應(yīng)將此 ID 字段設(shè)置為以增量方式自動(dòng)增加的標(biāo)識(shí)列。這樣,SQL Server 就可以為添加到表中的每條記錄自動(dòng)生成一個(gè)唯一的整數(shù)值。要進(jìn)行此設(shè)置,請(qǐng)?jiān)谠摿猩蠁螕羰髽?biāo)右鍵,從上下文相關(guān)菜單中選擇 Properties(屬性),然后在 Property Pages(屬性頁(yè))對(duì)話框中選擇 Columns(列)選項(xiàng)卡。其他的操作就很容易了(參見(jiàn)圖 4)。

圖 4:Property Pages(屬性頁(yè))對(duì)話框中的 Columns(列)選項(xiàng)卡
使用“添加主題記錄”和“添加解答記錄”方案中的信息,可以創(chuàng)建其他兩個(gè)表。請(qǐng)務(wù)必為每個(gè)表創(chuàng)建 ID 列,并將這些列標(biāo)記為標(biāo)識(shí)列和主鍵。下面的圖 5 顯示了三個(gè)已完成的表。

圖 5:三個(gè)已完成的表
您會(huì)發(fā)現(xiàn),這些表都通過(guò)連接線與數(shù)據(jù)庫(kù)圖連接起來(lái)。這些連接線表明表之間存在外鍵關(guān)系。例如,Questions(問(wèn)題)表中的 TopicID 列與 Topics(主題)表中的 ID 列相關(guān)聯(lián)。通過(guò)將這種關(guān)系存儲(chǔ)到數(shù)據(jù)庫(kù)中,您可以制定用以防止在數(shù)據(jù)庫(kù)中保存非法數(shù)據(jù)的規(guī)則。本示例中的關(guān)系規(guī)則是,Questions.TopicID 列所允許的有效值只能是 Topics.ID 列中已存在的某條記錄的值。
您可以通過(guò)將 Questions(問(wèn)題)表中的 TopicID 列拖放到 Topics(主題)表中的 ID 列上,來(lái)定義這些關(guān)系。此時(shí)將出現(xiàn)一個(gè)對(duì)話框,顯示規(guī)則定義的詳細(xì)信息并要求您按下 OK(確定)按鈕進(jìn)行確認(rèn)(參見(jiàn)圖 6)。

圖 6:Create Relationship(創(chuàng)建關(guān)系)對(duì)話框
您可能會(huì)發(fā)現(xiàn),Responses.QuestionID 和 Questions.ID 之間也定義了一個(gè)關(guān)系。
注意:您可能已經(jīng)注意到,我們還沒(méi)有為專(zhuān)家定義任何表。我決定將有關(guān)專(zhuān)家的信息存儲(chǔ)在一個(gè) XML 文件中,而不是存儲(chǔ)在數(shù)據(jù)庫(kù)中。這樣做的主要原因是我們可以借此討論一下如何讀寫(xiě) XML 數(shù)據(jù),以便在同一個(gè)應(yīng)用中融合 XML 數(shù)據(jù)和關(guān)系數(shù)據(jù)。我們將在下一部分中討論有關(guān)專(zhuān)家數(shù)據(jù)的問(wèn)題。
至此,數(shù)據(jù)庫(kù)和表都已定義完畢。以上介紹了解決方案的實(shí)際數(shù)據(jù)存儲(chǔ)過(guò)程。但是,我們還需要了解如何在表中讀寫(xiě)信息。為此,我們將定義 SQL Server 中的存儲(chǔ)過(guò)程。
使用 Visual Studio .NET 2003 編寫(xiě)存儲(chǔ)過(guò)程
數(shù)據(jù)表定義了如何在數(shù)據(jù)庫(kù)中存儲(chǔ)數(shù)據(jù),但沒(méi)有說(shuō)明如何存取數(shù)據(jù)。我們還需要了解讀寫(xiě)記錄以便從表中再次調(diào)用選定行和列的詳細(xì)信息。開(kāi)發(fā)人員通常會(huì)在其代碼中編寫(xiě)一些特殊的查詢(xún)語(yǔ)句,用于讀寫(xiě)數(shù)據(jù)。這不僅會(huì)導(dǎo)致效率低下,還會(huì)帶來(lái)安全性問(wèn)題。在本應(yīng)用中,所有數(shù)據(jù)存取工作都將通過(guò) SQL Server 存儲(chǔ)過(guò)程(stored procedures,有時(shí)稱(chēng)作“stored procs”或“sprocs”)來(lái)處理。使用存儲(chǔ)過(guò)程可以提高解決方案的性能并使之更安全。此外,使用存儲(chǔ)過(guò)程可以增加數(shù)據(jù)層的抽象級(jí)別,從而保護(hù)解決方案的其他部分不受小的數(shù)據(jù)布局和格式變化帶來(lái)的影響。這樣可使您的解決方案更可靠,更易于維護(hù)。
為什么不使用特殊的查詢(xún)語(yǔ)句
我們經(jīng)常會(huì)看到如下所示的文章和代碼示例:
Private Function GetSomeData(ByVal ID As Integer) As SqlDataReader
Dim strSQL As String
strSQL = "SELECT * FROM MyTable WHERE ID=" & ID.ToString()
cd = New SqlCommand
With cd
.CommandText = strSQL
.CommandType = CommandType.Text
.Connection = cn
.Connection.Open()
Return .ExecuteReader(CommandBehavior.CloseConnection)
End With
End Function
上述代碼不符合要求的原因有以下幾個(gè)。首先,如果將 SQL 查詢(xún)語(yǔ)句嵌套在代碼中,那么只要數(shù)據(jù)層發(fā)生任何變化,都必須編輯并重新編譯代碼層。這樣就會(huì)帶來(lái)諸多不便。還可能會(huì)導(dǎo)致其他錯(cuò)誤,而且通常會(huì)造成數(shù)據(jù)服務(wù)和代碼之間的混亂。
其次,如果使用不經(jīng)過(guò)輸入驗(yàn)證的字符串連接 ("...WHERE ID=" & ID.ToString()),將可能使您的應(yīng)用程序暴露在黑客的攻擊之下。更重要的是,這樣就會(huì)為惡意用戶(hù)提供了在您的代碼中添加其他 SQL 關(guān)鍵字的機(jī)會(huì)。例如,根據(jù)您的輸入模式,惡意用戶(hù)不僅可以輸入 13 或 21 作為有效的表 ID,還可以輸入 13; DELETE FROM USERS 或其他可能會(huì)帶來(lái)危害的語(yǔ)句。完善的輸入驗(yàn)證可以保護(hù)您的系統(tǒng)免受大多數(shù) SQL 插入代碼的攻擊,所以最好將所有內(nèi)置的 SQL 語(yǔ)句完全刪除,使攻擊者很難濫用您的應(yīng)用程序數(shù)據(jù)。
最后,內(nèi)置 SQL 語(yǔ)句的執(zhí)行速度要比存儲(chǔ)過(guò)程慢得多。創(chuàng)建存儲(chǔ)過(guò)程并將其存儲(chǔ)到數(shù)據(jù)庫(kù)中時(shí),SQL Server 會(huì)對(duì)其文本進(jìn)行評(píng)估并以?xún)?yōu)化的形式進(jìn)行存儲(chǔ),從而使之更容易在運(yùn)行時(shí)為 SQL Server 所用。如果使用內(nèi)置的特殊查詢(xún)語(yǔ)句,就必須在每次運(yùn)行該代碼之前進(jìn)行這種評(píng)估。對(duì)于那些供大量用戶(hù)使用的應(yīng)用程序而言,每分鐘就可能需要對(duì)同一查詢(xún)語(yǔ)句進(jìn)行數(shù)百次評(píng)估。
相反,存儲(chǔ)過(guò)程可以保持代碼的簡(jiǎn)潔明了,可以提供額外的安全保護(hù),并能提高解決方案的性能。這些都是摒棄內(nèi)置查詢(xún)語(yǔ)句而使用存儲(chǔ)過(guò)程的原因。
將存儲(chǔ)過(guò)程添加到 Visual Studio .NET 數(shù)據(jù)庫(kù)項(xiàng)目中
使用 Visual Studio .NET 2003 創(chuàng)建存儲(chǔ)過(guò)程非常簡(jiǎn)單。首先,您需要打開(kāi)一個(gè)數(shù)據(jù)庫(kù)項(xiàng)目。這一操作已在本文第一部分中完成。然后,您可以使用代碼模板創(chuàng)建存儲(chǔ)過(guò)程,也可以針對(duì) Server Explorer(服務(wù)器資源管理器)窗口中連接的數(shù)據(jù)庫(kù),使用 Visual Studio .NET 2003 直接編輯新的存儲(chǔ)過(guò)程。本文重點(diǎn)介紹如何針對(duì)連接的數(shù)據(jù)庫(kù)服務(wù)器直接編輯存儲(chǔ)過(guò)程。稍后會(huì)介紹如何為以后的遠(yuǎn)程服務(wù)器安裝生成所有結(jié)果腳本。
介紹使用 Visual Studio .NET 2003 編寫(xiě)存儲(chǔ)過(guò)程的機(jī)制之前,還要重點(diǎn)強(qiáng)調(diào)一下與創(chuàng)建可靠的存儲(chǔ)過(guò)程相關(guān)的幾個(gè)一般問(wèn)題。首先,最好將創(chuàng)建和執(zhí)行存儲(chǔ)過(guò)程的整個(gè)過(guò)程看作是多層應(yīng)用程序模型的一個(gè)成熟成員。存儲(chǔ)過(guò)程提供了一種對(duì)您的數(shù)據(jù)存取進(jìn)行編程的方法。這樣,您可以更好地控制整個(gè)解決方案并提高其效率。也就是說(shuō),應(yīng)將存儲(chǔ)過(guò)程集合看作是應(yīng)用程序中一個(gè)獨(dú)立的層。優(yōu)秀的數(shù)據(jù)存取策略應(yīng)允許存儲(chǔ)過(guò)程作為獨(dú)立的組件而存在。也就是說(shuō),存儲(chǔ)過(guò)程層中需要具備安全性、錯(cuò)誤處理以及其他構(gòu)成優(yōu)秀組件層的詳細(xì)內(nèi)容。更重要的是,應(yīng)像在其他高級(jí)編程環(huán)境中那樣訪問(wèn) T-SQL 語(yǔ)言,而不是僅僅將其作為一種生成數(shù)據(jù)庫(kù)查詢(xún)的方式。
注意:現(xiàn)在,我懷疑有些讀者可能在想他們并不打算對(duì) SQL Server 進(jìn)行編程,或者認(rèn)為這項(xiàng)工作最好留給那些 DBA 們來(lái)完成。雖然具備數(shù)據(jù)庫(kù)管理員經(jīng)驗(yàn)會(huì)有所幫助,但并一定非要成為火箭科學(xué)家(這里指技藝高超的編程專(zhuān)家)才能很好地完成 SQL Server 編程工作。像其他語(yǔ)言一樣,這種語(yǔ)言也需要花費(fèi)一定的時(shí)間并通過(guò)一定的實(shí)踐才能熟練掌握,在這一點(diǎn)上它與其他語(yǔ)言并沒(méi)有太大的不同。如果您能夠在 Microsoft Visual Basic? .NET 中編程,也就能夠在 T-SQL 中編程。
使用 Visual Studio .NET 添加存儲(chǔ)過(guò)程
下面詳細(xì)介紹如何在 Visual Studio .NET 2003 中將存儲(chǔ)過(guò)程添加到現(xiàn)有 SQL Server 數(shù)據(jù)庫(kù)中。您需要使用服務(wù)器資源管理器打開(kāi)一個(gè)新的存儲(chǔ)過(guò)程模板,進(jìn)行編輯,然后再將其保存到數(shù)據(jù)庫(kù)中。下面是分步實(shí)現(xiàn)這一過(guò)程的示例:
打開(kāi) Visual Studio .NET,然后打開(kāi)一個(gè)現(xiàn)有的數(shù)據(jù)庫(kù)項(xiàng)目(如本文前面所啟動(dòng)的項(xiàng)目)或啟動(dòng)一個(gè)新項(xiàng)目。
在 Server Explorer(服務(wù)器資源管理器)中,展開(kāi) Data Connections(數(shù)據(jù)連接)樹(shù),找到您要使用的數(shù)據(jù)庫(kù) (DotNetKB),然后在 Stored Procedures(存儲(chǔ)過(guò)程)節(jié)點(diǎn)上單擊鼠標(biāo)右鍵,打開(kāi)上下文相關(guān)菜單。
從上下文相關(guān)菜單中選擇 New Stored Procedure(新建存儲(chǔ)過(guò)程),在 Visual Studio .NET 編輯器空間中打開(kāi)一個(gè)存儲(chǔ)過(guò)程模板。現(xiàn)在,可以鍵入內(nèi)容了。
完成編輯后,只需關(guān)閉編輯器中正在編輯的頁(yè)面,Visual Studio .NET 將使用存儲(chǔ)過(guò)程的名稱(chēng)將該項(xiàng)內(nèi)容保存到數(shù)據(jù)庫(kù)中。如果鍵入的內(nèi)容有誤,編輯器會(huì)向您報(bào)告這些錯(cuò)誤,您可以在保存存儲(chǔ)過(guò)程之前修正這些錯(cuò)誤(參見(jiàn)圖 11)。
下面是存儲(chǔ)過(guò)程的一個(gè)簡(jiǎn)單示例,它返回一個(gè)主題列表。
CREATE PROCEDURE TopicsGetList
AS
SET NOCOUNT ON -- 不返回受影響行的值
SELECT
ID,
Title,
Description
FROM
Topics
ORDER BY
Title
RETURN @@ERROR
在本示例中,有幾點(diǎn)需要指出。首先,請(qǐng)注意 SET NOCOUNT ON 行。它告訴 SQL Server 停止為該查詢(xún)計(jì)算受影響的行數(shù),并停止向調(diào)用函數(shù)返回該值。這是一項(xiàng)不必要的額外工作。其次,結(jié)尾處的 RETURN @@ERROR 一行很重要。此行代碼返回 SQL Server 中發(fā)生的錯(cuò)誤的整數(shù)值。您可以在調(diào)用例程中使用此代碼完成其他診斷和錯(cuò)誤處理操作。您現(xiàn)在并不需要執(zhí)行任何操作,但它們是創(chuàng)建存儲(chǔ)過(guò)程時(shí)應(yīng)該遵循的兩個(gè)好習(xí)慣。
下面是一個(gè)更復(fù)雜的存儲(chǔ)過(guò)程。此過(guò)程用于從數(shù)據(jù)庫(kù)中檢索單條主題記錄。您會(huì)發(fā)現(xiàn)一些附加項(xiàng),包括輸入?yún)?shù)、返回特定值的輸出參數(shù),以及檢查輸入?yún)?shù)并在需要時(shí)返回錯(cuò)誤的某些程序代碼。
CREATE PROCEDURE TopicsGetItem
(
@AdminCode char(3),
@ID int,
@Title varchar(30) OUTPUT,
@Description varchar(500) OUTPUT
)
AS
SET NOCOUNT ON -- 不返回受影響行的值
-- 確保是一個(gè) Admin 用戶(hù)
IF @AdminCode<>‘a(chǎn)dm‘
BEGIN
RETURN 100 -- 無(wú)效 admin 錯(cuò)誤
END
-- 檢查記錄是否存在
IF (SELECT Count(ID) FROM Topics WHERE ID=@ID)=0
BEGIN
RETURN 101 --- 無(wú)效 ID 代碼
END
-- 繼續(xù)執(zhí)行并返回該記錄
SELECT
@Title=Title,
@Description=Description
FROM
Topics
WHERE
ID=@ID
-- 返回錯(cuò)誤,如果成功則返回 0
RETURN @@ERROR
在本示例中,還有幾點(diǎn)需要指出。首先,您會(huì)在存儲(chǔ)過(guò)程頂端看到一個(gè)參數(shù)列表。除前兩個(gè)參數(shù)外,其他參數(shù)均被標(biāo)記為 OUTPUT 參數(shù)。這些參數(shù)用于返回選定記錄的值。使用一條記錄的返回值要比返回帶有所有字段的記錄集合更為高效。
其次,您會(huì)發(fā)現(xiàn)用于檢查 @AdminCode 參數(shù)值的 T-SQL 數(shù)據(jù)塊,以確保傳遞正確的代碼。如果傳遞的代碼不正確,則傳遞返回代碼 100 并停止執(zhí)行該過(guò)程。再其次,您會(huì)發(fā)現(xiàn)檢查 @ID 參數(shù),以確保其代表一條現(xiàn)有記錄。如果不是現(xiàn)有記錄,則傳送返回代碼 101 并終止執(zhí)行。最后,如果輸入變量都有效,存儲(chǔ)過(guò)程將嘗試選擇記錄并返回相應(yīng)的值。如果此時(shí)發(fā)生任何錯(cuò)誤,將由該過(guò)程的最后一行代碼進(jìn)行處理。
注意:通常情況下,最好將自定義錯(cuò)誤代碼及其含義保存在數(shù)據(jù)庫(kù)中的一個(gè)單獨(dú)的表格中,或保存在解決方案可以訪問(wèn)的文本文件中。這樣就可以輕松更新這些錯(cuò)誤代碼,并與解決方案中的其他子系統(tǒng)共享。因?yàn)檫@只是一個(gè)短小的示例,其中只使用了兩個(gè)錯(cuò)誤代碼,所以我決定創(chuàng)建一個(gè)包含大量代碼和消息的文檔,以供其他子系統(tǒng)參考。
該解決方案中包含的存儲(chǔ)過(guò)程超過(guò) 25 個(gè)。本文僅舉一例進(jìn)行說(shuō)明,其他代碼可以通過(guò)本文開(kāi)始處的鏈接進(jìn)行下載。最后這個(gè)示例使用一個(gè)自定義的內(nèi)置標(biāo)量函數(shù)。
使用自定義標(biāo)量函數(shù)
有時(shí),單獨(dú)一個(gè)存儲(chǔ)過(guò)程不足以解決問(wèn)題。例如,我們的用戶(hù)方案中就有一個(gè)方案要求列出某個(gè)問(wèn)題的解答數(shù)目。解決此問(wèn)題的方法之一是生成一個(gè)對(duì)問(wèn)題的解答進(jìn)行計(jì)數(shù)的子查詢(xún)。另外一種方法是生成一個(gè)自定義函數(shù),返回標(biāo)量值并將其包含在問(wèn)題查詢(xún)中。這種方法還有一個(gè)好處,那就是我們可以在其他存儲(chǔ)過(guò)程中再次使用該標(biāo)量函數(shù)。
添加自定義函數(shù)的操作類(lèi)似于添加存儲(chǔ)過(guò)程。在 Server Explorer(服務(wù)器資源管理器)樹(shù)中,在選定數(shù)據(jù)庫(kù)的 Functions(函數(shù))節(jié)點(diǎn)上單擊鼠標(biāo)右鍵,然后從上下文相關(guān)菜單中選擇 New Scalar-Valued Function(新建標(biāo)量值函數(shù))。然后在編輯器中編輯該文檔,并像保存存儲(chǔ)過(guò)程那樣保存該文檔。
以下是自定義函數(shù)的代碼:
CREATE FUNCTION dbo.fn_QuestionsGetResponseCount
(
@ID int
)
RETURNS int
AS
BEGIN
DECLARE @ResponseCount int
Set @ResponseCount =
(
SELECT
COUNT(Responses.ID)
FROM
Responses
WHERE
Responses.QuestionID=@ID
)
RETURN @ResponseCount
END
以下是使用自定義函數(shù)的存儲(chǔ)過(guò)程:
CREATE PROCEDURE QuestionsGetCountWithNoResponses
(
@Total int OUTPUT
)
AS
SET NOCOUNT ON -- 不返回受影響行的值
SELECT
@Total=Count(ID)
FROM
Questions
WHERE
dbo.fn_QuestionsGetResponseCount(Questions.ID)=0
RETURN @@ERROR
了解如何編寫(xiě)存儲(chǔ)過(guò)程和自定義函數(shù)之后,我們還將討論使用 Visual Studio .NET 2003 創(chuàng)建數(shù)據(jù)層時(shí)的另一個(gè)問(wèn)題,即安全性問(wèn)題。
IIS、ASP.NET 和 SQL Server 的安全性問(wèn)題
SQL Server、Internet 信息服務(wù)器和 ASP.NET 引擎都提供了堅(jiān)實(shí)可靠的安全模型,它們可以很好地在一起協(xié)同工作。為了保證用戶(hù)數(shù)據(jù)和應(yīng)用程序的安全,Microsoft 還為每項(xiàng)服務(wù)的默認(rèn)設(shè)置設(shè)置了相當(dāng)?shù)偷闹?。大多?shù)開(kāi)發(fā)人員面臨的挑戰(zhàn)是如何使用 SQL Server、IIS 和 ASP.NET 在應(yīng)用程序和數(shù)據(jù)之間設(shè)置適當(dāng)?shù)男湃渭?jí)別,而不會(huì)留下可被別人輕易攻入的安全漏洞。由于涉及三類(lèi)服務(wù)(SQL Server、IIS 和 ASP.NET),所以需要采取三個(gè)關(guān)鍵的步驟來(lái)確保解決方案的安全。本部分討論一種為 Web 應(yīng)用程序設(shè)置足夠權(quán)限和信任級(jí)別的更常用(且可靠)的方法。
注意:關(guān)于安全性和 Web 解決方案這個(gè)大主題,本系列文章難以展開(kāi)較充分的討論。要更好地理解此問(wèn)題和可能的解決方案,請(qǐng)參閱安全 ASP.NET 應(yīng)用程序的創(chuàng)建模式和實(shí)踐系列文章:驗(yàn)證、授權(quán)和安全通信。
定義 DotNetKB 自定義 IIS 用戶(hù)帳戶(hù)。
保證 Web 應(yīng)用程序安全性的最安全的方法是定義一個(gè)權(quán)限有限的自定義用戶(hù),然后對(duì) IIS 進(jìn)行配置,使之能夠在執(zhí)行您的 Web 應(yīng)用程序時(shí)能作為自定義用戶(hù)運(yùn)行。這是相當(dāng)容易實(shí)現(xiàn)的,可以確保訪問(wèn)您的 Web 應(yīng)用程序的每個(gè)訪問(wèn)者都只具有您希望他們具有的權(quán)限。
第一步是生成一個(gè)新的 Windows 用戶(hù)(本例中稱(chēng)為 DotNetKB),為其設(shè)置一個(gè)增強(qiáng)型密碼,然后將其添加到 Windows 來(lái)賓組 (Guest Windows Group) 中。同時(shí),確保選中 Password never expires(密碼永不過(guò)期)和 User cannot change password(用戶(hù)不能更改密碼)復(fù)選框。這樣將生成一個(gè)權(quán)限有限的用戶(hù),在 IIS 中運(yùn)行您的 Web 應(yīng)用程序時(shí),您可以將其用作標(biāo)識(shí)(參見(jiàn)圖 7)。

圖 7:生成的權(quán)限有限的用戶(hù)
然后,調(diào)用 Internet 信息服務(wù)器管理員并選擇承載這些網(wǎng)頁(yè)的 Web 應(yīng)用程序。在本例中,您可以選擇承載前文所生成的測(cè)試頁(yè)的 Web 應(yīng)用程序 (DotNetKB_WebSite)。在樹(shù)視圖中的 Web 應(yīng)用程序上單擊鼠標(biāo)右鍵,然后從上下文相關(guān)菜單中選擇 Properties...(屬性...)。然后選擇 Directory Security(目錄安全性)并單擊該對(duì)話框 Anonymous access and authentication control(匿名訪問(wèn)和驗(yàn)證控制)部分中的 Edit(編輯)按鈕。最后,輸入自定義用戶(hù)名 (DotNetKB),取消選擇 Allow IIS to control password(允許 IIS 控制密碼)復(fù)選框,并輸入該自定義用戶(hù)帳戶(hù)的密碼。完成所有這些工作之后,單擊 OK(確定)按鈕,將這些更改保存到 IIS 配置數(shù)據(jù)庫(kù)中(參見(jiàn)圖 8)。

圖 8:Authentication Methods(驗(yàn)證方法)對(duì)話框
此時(shí),IIS 將在一個(gè)權(quán)限有限的自定義帳戶(hù)下運(yùn)行。任何訪問(wèn)者訪問(wèn)您應(yīng)用程序的網(wǎng)頁(yè)時(shí),都將以這個(gè)自定義用戶(hù)身份運(yùn)行,且只具有該自定義用戶(hù)的驗(yàn)證權(quán)限。
授權(quán) DotNetKB 用戶(hù)帳戶(hù)訪問(wèn) SQL Server
然后,您需要為該自定義用戶(hù)授予訪問(wèn)數(shù)據(jù)庫(kù) (DotNetKB) 的相應(yīng)權(quán)限。為此,您可以使用 Microsoft SQL Server 企業(yè)管理器或編寫(xiě)一個(gè)自定義腳本,以創(chuàng)建一個(gè)這樣的用戶(hù)并授予其訪問(wèn)特定對(duì)象的權(quán)限。本文介紹如何使用 SQL Server 企業(yè)管理器完成此操作。您還可以從后文中看到一個(gè)腳本示例。
注意:盡管 Visual Studio .NET 2003 具有與 SQL Server 兼容的許多強(qiáng)大的集成功能,但也不允許從 Visual Studio .NET 2003 中輕松管理用戶(hù)和用戶(hù)權(quán)限。在大型的組織和團(tuán)隊(duì)中,這些高級(jí)任務(wù)通常由數(shù)據(jù)庫(kù)管理員完成。
因此,啟動(dòng) SQL Server 企業(yè)管理器之后,您可以按照以下步驟將自定義用戶(hù) (DotNetKB) 添加數(shù)據(jù)庫(kù)中(參見(jiàn)圖 9):
在左側(cè)的樹(shù)視圖中,展開(kāi)節(jié)點(diǎn)以顯示 DotNetKB 數(shù)據(jù)庫(kù)。在我的計(jì)算機(jī)上,樹(shù)視圖的結(jié)構(gòu)如下:Console Root | SQL Server Group | (LOCAL) (Windows NT) | Databases | DotNetKB。
然后,在數(shù)據(jù)庫(kù)下的 Users(用戶(hù))節(jié)點(diǎn)上單擊鼠標(biāo)右鍵,并選擇 New Database User...(新建數(shù)據(jù)庫(kù)用戶(hù)...)。顯示 Database User Properties - New User(數(shù)據(jù)庫(kù)用戶(hù)屬性 - 新建用戶(hù))對(duì)話框時(shí),從 Login name(登錄名)下拉框中選擇 <new>(<新建>)。
顯示 SQL Server Login Properties - New Login(SQL Server 登錄屬性 - 新建登錄)對(duì)話框時(shí),選擇 General(常規(guī))選項(xiàng)卡,并在 Name(名稱(chēng))輸入框中輸入 DotNetKB。確保選中 Windows Authentication(Windows 驗(yàn)證)單選按鈕,并從 Domain(域)下拉框中選擇自定義用戶(hù)帳戶(hù)所在的計(jì)算機(jī)的名稱(chēng)。然后從 Database(數(shù)據(jù)庫(kù))下拉框中選擇 DotNetKB。
現(xiàn)在,選擇 Databases(數(shù)據(jù)庫(kù))選項(xiàng)卡,在對(duì)話框頂部的列表中找到 DotNetKB 數(shù)據(jù)庫(kù)并選中它。然后,確保選中對(duì)話框底部列表中的 public(公共)角色。最后,單擊對(duì)話框底部的 OK(確定)按鈕,保存您的更改。

圖 9:在數(shù)據(jù)庫(kù)中添加自定義用戶(hù)
然后,您需要為 DotNetKB 數(shù)據(jù)庫(kù)中的所有存儲(chǔ)過(guò)程和自定義函數(shù)添加執(zhí)行權(quán)限。為此,您只需為 public(公共)角色授予權(quán)限。您可以將權(quán)限授予 DotNetKB 用戶(hù),這樣將使以后的登錄(當(dāng)這些用戶(hù)獲得訪問(wèn) DotNetKB 的權(quán)限時(shí))更容易執(zhí)行存儲(chǔ)過(guò)程,而不需要為每個(gè)用戶(hù)添加新的權(quán)限。
下面是為 DotNetKB 數(shù)據(jù)庫(kù)中的存儲(chǔ)過(guò)程和函數(shù)授予執(zhí)行權(quán)限的步驟:
突出顯示樹(shù)視圖中 DotNetKB 數(shù)據(jù)庫(kù)下的 Users(用戶(hù))節(jié)點(diǎn),以顯示此數(shù)據(jù)庫(kù)的用戶(hù)列表。找到 DotNetKB 用戶(hù)并在其上雙擊,打開(kāi) Database Users Properties(數(shù)據(jù)庫(kù)用戶(hù)屬性)對(duì)話框。
突出顯示(選中)public(公共)角色時(shí),單擊 Properties...(屬性...)按鈕,打開(kāi) Database Role Properties(數(shù)據(jù)庫(kù)角色屬性)對(duì)話框。然后單擊 Permissions...(權(quán)限...)按鈕,顯示數(shù)據(jù)庫(kù)對(duì)象和權(quán)限設(shè)置列表。
選中對(duì)話框頂部 Database role(數(shù)據(jù)庫(kù)角色)下拉列表中的 public(公共)角色之后,找到為此數(shù)據(jù)庫(kù)定義的所有存儲(chǔ)過(guò)程和自定義函數(shù)(可能需要展開(kāi)對(duì)話框才能看到全名),并確保選中各項(xiàng)旁邊的 EXECUTE(執(zhí)行)復(fù)選框。您可能會(huì)發(fā)現(xiàn)某些系統(tǒng)對(duì)象的其他一些復(fù)選框也被選中了,請(qǐng)不要更改這些選項(xiàng)。
最后,設(shè)置所有的 EXECUTE(執(zhí)行)權(quán)限后,單擊 OK(確定)按鈕,保存更改并關(guān)閉對(duì)話框。依次單擊 OK(確定)按鈕,直到所有對(duì)話框均被關(guān)閉。
至此,您已為 IIS 創(chuàng)建了自定義用戶(hù),并設(shè)置了該用戶(hù)在 SQL Server 中的相應(yīng)權(quán)限。現(xiàn)在,您需要在 ASP.NET Web 項(xiàng)目中進(jìn)行一個(gè)配置更改,確保 ASP.NET 使用同一個(gè)用戶(hù)帳戶(hù)執(zhí)行對(duì) SQL Server 的所有調(diào)用。
設(shè)置您的 ASP.NET 應(yīng)用程序以模擬 DotNetKB 用戶(hù)
為 IIS 下運(yùn)行的 ASP.NET Web 應(yīng)用程序生成堅(jiān)實(shí)可靠的配置的最后一個(gè)步驟是:配置 ASP.NET Web 應(yīng)用程序,使之能夠接受來(lái)自 IIS 的 Windows 用戶(hù)標(biāo)識(shí)并能用于訪問(wèn)其他操作系統(tǒng)資源。為此,您只需在 web.config 根文件中輸入一行代碼。
注意:盡管目前我們還沒(méi)有真正開(kāi)發(fā)出用于承載我們的頁(yè)面的 ASP.NET Web 應(yīng)用程序,但您可以使用這些信息在生成測(cè)試頁(yè)的下一節(jié)中驗(yàn)證數(shù)據(jù)訪問(wèn)層的功能。
修改后的 web.config 文件如下所示:
<configuration>
<system.web>
... 其他要素 ...
<identity impersonate="true"/><!-- 假設(shè) IIS 用戶(hù)標(biāo)識(shí) -->
... 其他要素 ...
</system.web>
</configuration>
請(qǐng)注意,您只需添加 <identity> 元素并將模擬特性設(shè)置為 true(真)。不必輸入用戶(hù)帳戶(hù)或密碼,因?yàn)檫@些信息將由 IIS 提供。也就是說(shuō),即使其他人能夠讀取您的配置文件,他們也無(wú)法確定使用哪些標(biāo)識(shí)憑據(jù)來(lái)執(zhí)行您的 Web 應(yīng)用程序。
至此,您已生成了自定義用戶(hù),并為其設(shè)置了訪問(wèn) SQL Server 和 IIS 的相應(yīng)權(quán)限?,F(xiàn)在,我們來(lái)創(chuàng)建一些測(cè)試頁(yè),確保它能夠正常工作。從這里您可以看出一切正常。
創(chuàng)建 ASP.NET 測(cè)試頁(yè)
創(chuàng)建測(cè)試頁(yè)始終是訪問(wèn) SQL Server 數(shù)據(jù)層并驗(yàn)證輸入和輸出參數(shù)是否得到正確處理的好辦法。實(shí)際上,這是確保以后的生產(chǎn)解決方案中的 ASP.NET 頁(yè)和組件能夠按照預(yù)期方式工作的唯一辦法。這對(duì)于從解決方案中的某個(gè)層調(diào)用其他層時(shí)的驗(yàn)證信任邊界和安全性問(wèn)題尤其正確。
另外,在進(jìn)行測(cè)試時(shí),請(qǐng)勿拘泥于創(chuàng)建生產(chǎn)類(lèi)接口。您只需測(cè)試目標(biāo)方法。實(shí)際上,故意創(chuàng)建一些您不愿以之為最終生產(chǎn)解決方案的“丑陋”測(cè)試頁(yè)是一個(gè)好的策略!本文中,我創(chuàng)建了一些非常簡(jiǎn)單的 ASP.NET 頁(yè),其中包含一個(gè)測(cè)試記錄列表和一個(gè)用于添加、編輯和刪除測(cè)試記錄的輸入表單。
例如,以下是用于測(cè)試主題記錄的 WebForm 布局。您會(huì)發(fā)現(xiàn),它包含錯(cuò)誤消息或其他消息的狀態(tài)標(biāo)簽、記錄計(jì)數(shù)標(biāo)簽、顯示記錄列表的數(shù)據(jù)網(wǎng)格、用于輸入檢索時(shí)使用的記錄 ID 的輸入框以及支持添加、編輯和刪除記錄的小表格(參見(jiàn)圖 10)。

圖 10:用于測(cè)試主題記錄的 WebForm 布局
在創(chuàng)建測(cè)試頁(yè)時(shí),最好使代碼簡(jiǎn)潔明了。我通常會(huì)為每個(gè)按鈕添加一小段代碼,以調(diào)用本地方法來(lái)處理數(shù)據(jù)庫(kù)操作。以下是 TopicTest.aspx 頁(yè)上 Get Record(獲取記錄)按鈕的代碼。
Private Sub btnGetTopic_Click(ByVal sender As System.Object,ByVal e As System.EventArgs) Handles btnGetTopic.Click
Try
Dim ID As Integer = Int32.Parse(txQueryID.Text)
GetItem(ID) ‘ 進(jìn)行數(shù)據(jù)庫(kù)調(diào)用
txID.Text = txQueryID.Text
txTitle.Text = mTitle
txDescription.Text = mDescription
lbStatus.Text = "success!"
Catch ex As Exception
lbStatus.Text = ex.Message
End Try
End Sub
請(qǐng)注意,本方法中實(shí)際執(zhí)行的唯一操作是由 GetItem(ID) 方法調(diào)用處理的。它執(zhí)行數(shù)據(jù)庫(kù)調(diào)用并使用返回的值設(shè)置本地變量。以下是 GetItem 方法的代碼。請(qǐng)注意,它使用了大量的 SqlParameter 對(duì)象來(lái)處理輸入和輸出值。
Private Sub GetItem(ByVal ID As Integer)
Try
pr = New SqlParameter("RETURN_VALUE", SqlDbType.Int)
pr.Direction = ParameterDirection.ReturnValue
Dim pTitle As SqlParameter = New SqlParameter
With pTitle
.Direction = ParameterDirection.Output
.DbType = DbType.String
.ParameterName = "@Title"
.Size = 30
End With
Dim pDescription As SqlParameter = New SqlParameter
With pDescription
.Direction = ParameterDirection.Output
.DbType = DbType.String
.ParameterName = "@Description"
.Size = 500
End With
cd = New SqlCommand
With cd
.CommandText = "TopicsGetItem"
.CommandType = CommandType.StoredProcedure
.Parameters.Add(New SqlParameter("@AdminCode", "adm"))
.Parameters.Add(New SqlParameter("@ID", ID))
.Parameters.Add(pTitle)
.Parameters.Add(pDescription)
.Parameters.Add(pr)
.Connection = cn
.Connection.Open()
.ExecuteNonQuery()
.Connection.Close()
End With
‘ 檢查返回代碼
If Not pr.Value Is Nothing Then
Select Case Int32.Parse(pr.Value)
Case 100 : Throw New ApplicationException("Access violation")
Case 101 : Throw New ApplicationException("Invalid ID")
End Select
End If
‘ 設(shè)置返回值
mTitle = pTitle.Value.ToString()
mDescription = pDescription.Value.ToString()
Catch ex As Exception
Throw New Exception(ex.Message, ex)
End Try
End Sub
GetItem 方法的另一個(gè)重要方面是使用了返回值參數(shù)。它在前幾行代碼中進(jìn)行聲明,并在執(zhí)行存儲(chǔ)過(guò)程后進(jìn)行檢查。請(qǐng)注意,我檢查了已知錯(cuò)誤代碼 100 和 101。有關(guān)其他錯(cuò)誤的處理方法,我們將在以后介紹如何創(chuàng)建成熟的中間層時(shí)進(jìn)行介紹。問(wèn)題在于,我要利用返回值并在需要時(shí)拋出一個(gè)自定義異常。
對(duì)于本解決方案示例,我最終生成了六個(gè) Web 表單,并用它們測(cè)試了將近 30 個(gè)存儲(chǔ)過(guò)程和自定義函數(shù)。您可在本文開(kāi)始部分列出的下載軟件包中找到所有這些完成的表單。
現(xiàn)在我們已經(jīng)定義了表、創(chuàng)建了存儲(chǔ)過(guò)程和函數(shù)并生成了 ASP.NET Web 表單,因此可以使用 Visual Studio .NET 2003 生成數(shù)據(jù)庫(kù)層的安裝腳本了。數(shù)據(jù)庫(kù)管理員(有時(shí)是您自己)可以將此腳本應(yīng)用到生產(chǎn)服務(wù)器上。
生成源代碼和安裝腳本
Visual Studio .NET 的另一個(gè)重要功能是它能夠?yàn)楝F(xiàn)有數(shù)據(jù)庫(kù)生成一個(gè)完整的生成腳本。實(shí)際上,您可以使用 Visual Studio .NET 為整個(gè)數(shù)據(jù)庫(kù)層生成源代碼(包括生成表和索引、授權(quán)、存儲(chǔ)過(guò)程等),還可以生成一個(gè)可用于在現(xiàn)有 SQL Server 上安裝這些數(shù)據(jù)庫(kù)對(duì)象的命令行腳本。
生成安裝腳本非常容易,它包括兩個(gè)步驟:首先,需要生成 T-SQL 腳本來(lái)創(chuàng)建數(shù)據(jù)庫(kù)對(duì)象(表、索引、過(guò)程等)。然后,生成一個(gè)針對(duì)目標(biāo) SQL Server 執(zhí)行 T-SQL 腳本的命令行腳本。
生成 T-SQL 腳本
生成安裝腳本之前,需要生成一個(gè)腳本集合,包括創(chuàng)建數(shù)據(jù)庫(kù)中的所有對(duì)象(表、索引、約束條件、用戶(hù)等)。

圖 11:生成腳本集合
以下是生成 T-SQL 腳本的步驟:
在 Server Explorer(服務(wù)器資源管理器)中,在選定的數(shù)據(jù)庫(kù)節(jié)點(diǎn) (DotNetKB) 上單擊鼠標(biāo)右鍵,然后從上下文相關(guān)菜單中選擇 Generate Create Script...(生成創(chuàng)建腳本...),打開(kāi) Generate Create Scripts(生成創(chuàng)建腳本)對(duì)話框。
在 General(常規(guī))選項(xiàng)卡上,選中 Script all objects(編寫(xiě)全部對(duì)象腳本)復(fù)選框。
在 Formatting(格式化)選項(xiàng)卡上,選中除最后一個(gè)復(fù)選框以外的所有復(fù)選框(僅與 7.0 版腳本兼容的功能)。僅在您的目標(biāo)服務(wù)器是 SQL Server 7.0 而不是 SQL Server 2000 時(shí),才需要最后一項(xiàng)。
在 Options(選項(xiàng))選項(xiàng)卡上,在 Security Scripting Options(安全性腳本選項(xiàng))部分,選中除 Script SQL Server logins(撰寫(xiě) SQL Server 登錄腳本)之外的所有選項(xiàng)。確保選中 Table Scripting Options(表腳本選項(xiàng))部分中的所有復(fù)選框。同時(shí)保留 File Format(文件格式)和 Files to Generate(生成的文件)的默認(rèn)單選按鈕。最后,當(dāng)所有設(shè)置均已設(shè)置正確時(shí),單擊 OK(確定)按鈕開(kāi)始腳本生成過(guò)程。
系統(tǒng)將提示您指定文件位置。默認(rèn)情況下,Visual Studio .NET 將指向現(xiàn)有數(shù)據(jù)庫(kù)項(xiàng)目中的 Create Scripts(創(chuàng)建腳本)文件夾。單擊 OK(確定)按鈕接受此默認(rèn)位置。
該過(guò)程完成后,您將獲得保存數(shù)據(jù)庫(kù)中各對(duì)象的文件列表。此腳本集合還包含了用于創(chuàng)建相應(yīng)的用戶(hù)并為其授予正確權(quán)限的腳本。您甚至可以將這些信息保存到 Visual SourceSafe 中,用于處理以后的版本問(wèn)題。最后,您可以根據(jù)需要將這些文件傳送給其他人,使他們可以直接更新或更改這些文件。至此,已經(jīng)完成了數(shù)據(jù)庫(kù)層的完整源代碼。
生成安裝腳本
最后一個(gè)步驟是讓 Visual Studio .NET 2003 生成一個(gè)命令行腳本,用于讀取所有 T-SQL 腳本并根據(jù)目標(biāo) SQL Server 運(yùn)行這些腳本。為此,需要完成以下步驟。
在 Solution Explorer(解決方案資源管理器)中,在項(xiàng)目名稱(chēng) (DotNetKB) 上單擊鼠標(biāo)右鍵,然后從上下文相關(guān)菜單中選擇 Create Command File...(創(chuàng)建命令文件...),打開(kāi) Create Command File(創(chuàng)建命令文件)對(duì)話框。
如果需要,可以更新 Name of Command File(命令文件名稱(chēng))輸入框,然后選擇合適的驗(yàn)證方案(除非您需要遠(yuǎn)程連接服務(wù)器,否則請(qǐng)使用 Microsoft Windows? NT?)。最后,單擊 Add All(全部添加)按鈕,以便將所有 T-SQL 腳本都包含在安裝操作中。
然后,單擊 OK(確定)按鈕生成腳本。這樣即可將完整的腳本加載到編輯器窗口(參見(jiàn)圖 12)中,您可以在該窗口中檢查腳本,所做的更改將在您關(guān)閉窗口時(shí)得到保存。

圖 12:生成安裝腳本
使用此腳本和 T-SQL 文件集,現(xiàn)在您可以將新數(shù)據(jù)庫(kù)層安裝到任何您具有相應(yīng)權(quán)限的目標(biāo) SQL Server 2000 上了。
小結(jié)
本部分討論了很多內(nèi)容。包括如何使用 Visual Studio .NET 2003 創(chuàng)建數(shù)據(jù)庫(kù)項(xiàng)目,如何創(chuàng)建新數(shù)據(jù)庫(kù)以及定義表、索引、約束條件和關(guān)系的數(shù)據(jù)庫(kù)圖,還介紹了使用 Visual Studio .NET 2003 添加可以存取表中存儲(chǔ)的數(shù)據(jù)的存儲(chǔ)過(guò)程和自定義函數(shù)。通過(guò)本文的學(xué)習(xí),您還學(xué)會(huì)了如何使用自定義的 Windows 帳戶(hù)以及 IIS 和 Web 應(yīng)用程序中的 web.config 文件設(shè)置,在 SQL Server 和您的 ASP.NET 解決方案之間建立一種可靠的信任關(guān)系。本文最后還介紹了用于驗(yàn)證數(shù)據(jù)層程序設(shè)計(jì)的測(cè)試 Web 表單示例,并說(shuō)明了如何生成可用于在任何目標(biāo) SQL Server 上安裝完成的這個(gè)數(shù)據(jù)層的 T-SQL 腳本和命令行腳本。
也許您已經(jīng)注意到,數(shù)據(jù)庫(kù)層的相關(guān)討論中未涉及到專(zhuān)家記錄的存儲(chǔ)和再調(diào)用過(guò)程。這是因?yàn)槲覜Q定使用 XML 文件代替它。這樣,我們可以借此機(jī)會(huì)學(xué)習(xí)如何將 XML 作為數(shù)據(jù)源,以及如何將這種數(shù)據(jù)格式與 SQL Server 數(shù)據(jù)結(jié)合起來(lái)以創(chuàng)建一個(gè)完整的解決方案。在下一部分中,我們將定義 XML 存儲(chǔ)格式和讀寫(xiě)這種數(shù)據(jù)的組件層,還將學(xué)習(xí)有關(guān) XML 序列化以及 ASP.NET 中的內(nèi)置數(shù)據(jù)高速緩存服務(wù)的相關(guān)知識(shí)。