使用受控碼建立預存程序和使用者定義函式 (VB)
Microsoft SQL Server 2005 與.NET 公用語言執行階段整合,讓開發人員透過受控程式碼建立資料庫物件。 本教學課程示範如何使用 Visual Basic 或 C# 程式碼建立受控預存程序和受控使用者定義函數。 我們也了解這些版本的 Visual Studio 如何讓您可以偵錯此類受控資料庫物件。
簡介
Microsoft SQL Server 2005 等資料庫使用 Transact-Structured Query Language (T-SQL) 來插入、修改和檢索資料。 大多數資料庫系統都包含用於對一系列 SQL 陳述式進行分組的結構,然後這些陳述式可以作為單一可重複使用單元執行。 儲存過程就是一個例子。 另一個是使用者定義函數 (UDF),我們將在步驟 9 中更詳細地研究該構造。
從本質上來說,SQL 是為處理資料集而設計的。 SELECT
、UPDATE
和 DELETE
陳述式本質上適用於對應表中的所有記錄,並且僅受 WHERE
子句的限制。 然而,有許多語言功能是為一次處理一筆記錄和操作標量資料而設計的。 CURSOR
允許一次循環一組記錄。 字串運算函數如 LEFT
、CHARINDEX
和 PATINDEX
可以處理標量資料。 SQL 也包含控制流程陳述式,如 IF
和 WHILE
。
在 Microsoft SQL Server 2005 之前,預存程序和 UDF 只能定義為 T-SQL 陳述式的集合。 然而,SQL Server 2005 旨在提供與公共語言執行時間 (CLR) 的整合,這是所有 .NET 組件所使用的執行時間。 因此,可以使用受控程式碼建立 SQL Server 2005 資料庫中的預存程序和 UDF。 也就是說,您可以建立預存程序或 UDF 作為 Visual Basic 類別中的方法。 這可讓這些預存程序和 UDF 利用 .NET Framework 和您自己的自訂類別中的功能。
在本教學課程中,我們將研究如何建立受控預存程序和使用者定義函數以及如何將它們整合到我們的 Northwind 資料庫中。 讓我們開始吧!
注意
受控資料庫物件比 SQL 物件具有一些優勢。 語言的豐富性和熟悉性以及重複使用現有程式碼和邏輯的能力是主要優勢。 但是,在處理不涉及太多過程邏輯的資料集時,受控資料庫物件的效率可能會較低。 有關使用受控程式碼與 T-SQL 的優勢的更全面討論,請查看使用受控程式碼建立資料庫物件的優勢。
步驟 1:將 Northwind 資料庫移出 App_Data
到目前為止,我們的所有教學課程都使用了 Web 應用程式 App_Data
資料夾中的 Microsoft SQL Server 2005 Express Edition 資料庫檔案。 將資料庫放置在 App_Data
簡化的分發和運行這些教學課程中,因為所有文件都位於一個目錄中,並且不需要額外的配置步驟來測試教學課程。
然而,在本教學課程中,我們將 Northwind 資料庫移出 SQL Server 2005 Express Edition 資料庫執行個體和 App_Data
,並明確地將其註冊到該執行個體中。 雖然我們可以使用 App_Data
資料夾中的資料庫執行本教學課程的步驟,但透過將資料庫明確註冊到 SQL Server 2005 Express Edition 資料庫執行個體,可以使許多步驟變得更加簡單。
本教學課程的下載有兩個資料庫文件 NORTHWND.MDF
和 NORTHWND_log.LDF
,並放置在名為 DataFiles
的資料夾中。 如果您按照自己的教學課程實施,請關閉 Visual Studio 並將 NORTHWND.MDF
和 NORTHWND_log.LDF
檔案從網站的 App_Data
資料夾移至網站外部的資料夾。 將資料庫檔案移到另一個資料夾後,我們需要將 Northwind 資料庫註冊到 SQL Server 2005 Express Edition 資料庫執行個體。 這可以透過 SQL Server Management Studio 完成。 如果您的電腦上安裝了非 Express Edition 的 SQL Server 2005,那麼您可能已經安裝了 Management Studio。 如果您的電腦上只有 SQL Server 2005 Express Edition,請花點時間下載並安裝 Microsoft SQL Server Management Studio。
啟動 SQL Server Management Studio。 如圖 1 所示,Management Studio 首先詢問要連接到哪個伺服器。 輸入 localhost\SQLExpress 作為伺服器名稱,在“驗證”下拉清單中選擇“Windows 驗證”,然後按一下“連線”。
圖 1:連接到適當的資料庫執行個體
連線後,物件資源管理器視窗將列出有關 SQL Server 2005 Express Edition 資料庫執行個體的資訊,包括其資料庫、安全性資訊、管理選項等。
我們需要將 DataFiles
資料夾 (或您可能已將其移至的任何位置) 中的 Northwind 資料庫附加到 SQL Server 2005 Express Edition 資料庫執行個體。 右鍵點擊“資料庫”資料夾,然後從上下文選單中選擇“附加”選項。 這將開啟“附加資料庫”對話框。 按一下「新增」按鈕,向下鑽取到適當的 NORTHWND.MDF
檔案,然後按一下「確定」。 此時您的螢幕應類似圖 2。
圖 2:連接到適當的資料庫執行個體 (按一下查看全尺寸影像)
注意
透過 Management Studio 連線至 SQL Server 2005 Express Edition 執行個體時,「附加資料庫」對話方塊不允許您深入檢視使用者設定檔目錄,例如「我的文件」。 因此,請確保將 NORTHWND.MDF
和 NORTHWND_log.LDF
檔案放置在非使用者設定檔目錄中。
按一下「確定」按鈕附加資料庫。 「附加資料庫」對話方塊將關閉,物件資源管理器現在應列出剛剛附加的資料庫。 Northwind 資料庫的名稱很可能是 9FE54661B32FDD967F51D71D0D5145CC_LINE ARTICLES\DATATUTORIALS\VOLUME 3\CSHARP\73\ASPNET_DATA_TUTORIAL_75_CS\APP_DATA\NORTHWND.MDF
這樣的。 右鍵點擊資料庫並選擇“重新命名”,將資料庫重新命名為 Northwind。
圖 3:將資料庫重新命名為 Northwind
步驟 2:在 Visual Studio 中建立新的解決方案和 SQL Server 專案
為了在 SQL Server 2005 中建立受控預存程序或 UDF,我們將在類別中將預存程序和 UDF 邏輯編寫為 Visual Basic 程式碼。 程式碼編寫完成後,我們需要將此類編譯為程式集 (.dll
檔案),並將該組件註冊到 SQL Server 資料庫,然後在資料庫中建立預存程序或 UDF 物件,指向大會。 這些步驟都可以手動執行。 我們可以在任何文字編輯器中建立程式碼,使用 Visual Basic 編譯器 (vbc.exe
) 從命令列編譯它,使用 CREATE ASSEMBLY
命令或從 Management Studio 將其註冊到資料庫,並透過類似的方式新增預存程序或 UDF 物件。 幸運的是,Visual Studio 的 Professional 和 Team Systems 版本包含可自動執行這些任務的 SQL Server 專案類型。 在本教學課程中,我們將逐步使用 SQL Server 專案類型來建立受控預存程序和 UDF。
注意
如果您使用的是 Visual Web Developer 或 Visual Studio 標準版,則必須使用手動方法。 步驟 13 提供了手動執行這些步驟的詳細說明。 我鼓勵您在閱讀步驟 13 之前先閱讀步驟 2 到 12,因為這些步驟包含重要的 SQL Server 設定說明,無論您使用什麼版本的 Visual Studio,都必須套用這些說明。
首先開啟 Visual Studio。 從「檔案」選單中,選擇「新專案」以顯示「新專案」對話方塊 (請參閱圖 4)。 深入查看資料庫專案類型,然後從右側列出的範本中選擇建立新的 SQL Server 專案。 我選擇命名 ManagedDatabaseConstructs
專案並將其放置在名為 Tutorial75
的解決方案中。
圖 4:建立新的 SQL Server 專案 (按一下查看大圖)
按一下「新專案」對話方塊中的「確定」按鈕以建立解決方案和 SQL Server 專案。
SQL Server 專案與特定資料庫相關聯。 因此,在建立新的 SQL Server 專案後,我們會立即被要求指定此資訊。 圖 5 顯示了「新資料庫參考」對話框,該對話框已填寫,指向我們在步驟 1 中在 SQL Server 2005 Express Edition 資料庫執行個體中註冊的 Northwind 資料庫。
圖 5:將 SQL Server 專案與 Northwind 資料庫關聯
為了偵錯我們將在此專案中建立的受控預存程序和 UDF,我們需要為連線啟用 SQL/CLR 偵錯支援。 每當將 SQL Server 專案與新資料庫關聯時 (如圖 5 所示),Visual Studio 都會詢問我們是否要在連線上啟用 SQL/CLR 偵錯 (請參閱圖 6)。 按一下是。
圖 6:啟用 SQL/CLR 偵錯
此時,新的 SQL Server 專案已新增至解決方案。 它包含一個以名為 Test Scripts
的檔案命名的資料夾 Test.sql
,該資料夾用於偵錯在專案中建立的受控資料庫物件。 我們將在步驟 12 中查看偵錯。
現在,我們可以為專案新增新的受控預存程序和 UDF,但在此之前,我們首先將現有的 Web 應用程式包含在解決方案中。 從「檔案」選單中選擇「新增」選項,然後選擇「現有網站」。 瀏覽到適當的網站資料夾並按一下「確定」。 如圖 7 所示,這將更新解決方案以包含兩個專案:網站和 ManagedDatabaseConstructs
SQL Server 專案。
圖 7:解決方案資源管理器現在包含兩個專案
Web.config
中的 NORTHWNDConnectionString
值目前引用 App_Data
資料夾中的 NORTHWND.MDF
檔案。 由於我們從 SQL Server 2005 Express Edition 資料庫執行個體中刪除並明確註冊了該資料庫,因此我們需要相應地更新 App_Data
NORTHWNDConnectionString
值。 在網站中開啟 Web.config
檔案並變更 NORTHWNDConnectionString
值,以便連接字串顯示為:Data Source=localhost\SQLExpress;Initial Catalog=Northwind;Integrated Security=True
。 進行此更改後,您的 <connectionStrings>
部分 Web.config
應類似於以下內容:
<connectionStrings>
<add name="NORTHWNDConnectionString" connectionString=
"Data Source=localhost\SQLExpress;Initial Catalog=Northwind;
Integrated Security=True;Pooling=false"
providerName="System.Data.SqlClient" />
</connectionStrings>
注意
如同前面教學課程所討論的,當從用戶端應用程式 (例如 ASP.NET 網站) 偵錯 SQL Server 物件時,我們需要停用連線池。 上面顯示的連接字串禁用連接池 (Pooling=false
)。 如果您不打算從 ASP.NET 網站偵錯受控預存程序和 UDF,請啟用連線池。
步驟 3:建立受控預存程序
要將受控預存程序新增至 Northwind 資料庫,我們首先需要將預存程序建立為 SQL Server 專案中的方法。 在解決方案資源管理器中,以滑鼠右鍵按一下 ManagedDatabaseConstructs
專案名稱並選擇新增專案。 這將顯示「新增專案」對話框,其中列出了可以新增到專案中的受控資料庫物件的類型。 如圖 8 所示,這包括預存程序和使用者定義函數等。
讓我們先新增一個儲存過程,該過程僅傳回所有已停產的產品。 將新的預存程序檔案命名為 GetDiscontinuedProducts.vb
。
圖 8:新增一個名為的新預存程序 GetDiscontinuedProducts.vb
(點擊看大圖)
這將建立一個新的 Visual Basic 類別文件,其中包含以下內容:
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
Partial Public Class StoredProcedures
<Microsoft.SqlServer.Server.SqlProcedure()> _
Public Shared Sub GetDiscontinuedProducts ()
' Add your code here
End Sub
End Class
請注意,預存程序是作為名為 Partial
的類別文件中的 Shared
方法實現的 StoredProcedures
。 此外,GetDiscontinuedProducts
方法以屬性修飾,SqlProcedure
屬性 將該方法標記為預存程序。
以下程式碼建立一個 SqlCommand
物件並將 CommandText
設定為一個 SELECT
查詢,該查詢傳回 Products
表中 Discontinued
欄位等於 1 的產品的所有列。 然後它執行命令並將結果發送回用戶端應用程式。 將此程式碼新增到 GetDiscontinuedProducts
方法。
' Create the command
Dim myCommand As New SqlCommand()
myCommand.CommandText = _
"SELECT ProductID, ProductName, SupplierID, CategoryID, " & _
" QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, " & _
" ReorderLevel, Discontinued " & _
"FROM Products " & _
"WHERE Discontinued = 1"
' Execute the command and send back the results
SqlContext.Pipe.ExecuteAndSend(myCommand)
所有受控資料庫SqlContext
物件都可以存取代表呼叫者上下文的物件。 SqlContext
透過其 Pipe
屬性提供對 SqlPipe
物件的存取。 SqlPipe
物件用於在 SQL Server 資料庫和呼叫應用程式之間傳送資訊。 顧名思義,ExecuteAndSend
方法執行傳入 的 SqlCommand
物件並將結果發送回用戶端應用程式。
注意
受控資料庫物件最適合使用過程邏輯而不是基於集合的邏輯的預存程序和 UDF。 過程邏輯涉及逐行處理資料集或處理標量資料。 然而,我們剛剛建立的 GetDiscontinuedProducts
方法不涉及過程邏輯。 因此,最好將其實作為 T-SQL 預存程序。 它作為受控預存程序實現,以演示建立和部署受控預存程序所需的步驟。
步驟 4:部署受控預存程序
完成此程式碼後,我們準備將其部署到 Northwind 資料庫。 部署 SQL Server 專案將程式碼編譯成程序集,向資料庫註冊程序集,並在資料庫中建立對應的物件,將它們連結到程序集中的相應方法。 步驟 13 中更準確地說明了「部署」選項執行的確切任務集。 在解決方案資源管理器中以滑鼠右鍵按一下 ManagedDatabaseConstructs
專案名稱,然後選擇「部署」選項。 但是,部署失敗並出現以下錯誤:「EXTERNAL」附近的語法不正確。 您可能要將目前資料庫的相容性層級設成高一點的值,以啟用這項功能。 請參閱預存程序 sp_dbcmptlevel
的說明。
當嘗試向 Northwind 資料庫註冊組件時,會出現此錯誤訊息。 為了向 SQL Server 2005 資料庫註冊程序集,資料庫的相容性等級必須設定為 90。 預設情況下,新的 SQL Server 2005 資料庫的相容性等級為 90。 但是,使用 Microsoft SQL Server 2000 建立的資料庫的預設相容等級為 80。 由於 Northwind 資料庫最初是 Microsoft SQL Server 2000 資料庫,因此其相容性等級目前設定為 80,因此需要增加到 90 才能註冊受控資料庫物件。
若要更新資料庫的相容性級別,請在 Management Studio 中開啟「新查詢」視窗並輸入:
exec sp_dbcmptlevel 'Northwind', 90
點擊工具列中的「執行」圖示來執行上述查詢。
圖 9:更新 Northwind 資料庫的相容性等級 (按一下查看大圖)
更新相容性等級後,重新部署 SQL Server 專案。 這次部署應該完成且不會出現錯誤。
傳回 SQL Server Management Studio,右鍵點選物件資源管理器中的 Northwind 資料庫,然後選擇「刷新」。 接下來,深入查看 Programmability 資料夾,然後展開 Assemblies 資料夾。 如圖 10 所示,Northwind 資料庫現在包含由 ManagedDatabaseConstructs
專案產生的組件。
圖 10:ManagedDatabaseConstructs
程式集現已註冊到 Northwind 資料庫
還要展開「預存程序」資料夾。 在那裡您將看到一個名為 GetDiscontinuedProducts
的預存程序。 該預存程序是由部署過程建立的,並指向 ManagedDatabaseConstructs
程式集中的 GetDiscontinuedProducts
方法。 當執行 GetDiscontinuedProducts
預存程序時,它會依序執行 GetDiscontinuedProducts
方法。 由於這是受控預存程序,因此無法透過 Management Studio 進行編輯 (因此預存程序名稱旁邊有鎖定圖示)。
圖 11:預存程序列在 GetDiscontinuedProducts
預存程序資料夾中
在呼叫受控預存程序之前,我們還必須克服一個障礙:資料庫被配置為阻止執行受控程式碼。 透過開啟一個新的查詢視窗並執行 GetDiscontinuedProducts
預存程序來驗證這一點。 您將收到以下錯誤訊息: .NET Framework 中的使用者程式碼執行已停用。 啟用“clr啟用”配置選項。
若要檢查 Northwind 資料庫的設定資訊,請在 exec sp_configure
查詢視窗中輸入並執行命令。 這表示 clr 啟用設定目前設定為 0。
圖 12:clr 啟用設定目前設定為 0 (點選查看全尺寸影像)
請注意,圖 12 中的每個配置設定都列出了四個值:最小值和最大值以及配置值和運行值。 若要更新 clr 啟用設定的設定值,請執行下列命令:
exec sp_configure 'clr enabled', 1
如果重新執行 exec sp_configure
,您將看到上述陳述式將 clr 啟用設定的設定值更新為 1,但運行值仍設為 0。 為了使此配置變更生效,我們需要執行 RECONFIGURE
命令,該命令會將 運行值設定為當前配置值。 只需在查詢視窗中輸入 RECONFIGURE
並點擊工具列中的“執行”圖示即可。 如果您現在執行 exec sp_configure
,您應該會看到 clr 啟用設定的配置和運行值的值為 1。
啟用 clr 的配置完成後,我們就可以運行受控 GetDiscontinuedProducts
預存程序了。 在查詢視窗中輸入並執行命令 exec
GetDiscontinuedProducts
。 呼叫預存程序會導致 GetDiscontinuedProducts
方法中對應的受控程式碼執行。 此程式碼發出 SELECT
查詢以傳回所有已停產的產品,並將此資料傳回呼叫應用程序,在此執行個體中為 SQL Server Management Studio。 Management Studio 接收這些結果並將其顯示在「結果」視窗中。
圖 13:GetDiscontinuedProducts
預存程序傳回所有停產產品 (點選查看大圖)
步驟 5:建立接受輸入參數的受控預存程序
我們在這些教學課程中建立的許多查詢和預存程序都使用了參數。 例如,在為類型化資料集的 TableAdapters 建立新預存程序教學課程中,我們建立了一個名為 GetProductsByCategoryID
的預存程序,它接受名為 @CategoryID
的輸入參數。 然後,預存程序傳回 CategoryID
欄位與所提供 @CategoryID
參數的值相符的所有產品。
若要建立接受輸入參數的受控預存程序,只需在方法定義中指定這些參數即可。 為了說明這一點,讓我們為名為 GetProductsWithPriceLessThan
的專案新增另一個受控預存程序 ManagedDatabaseConstructs
。 此受控預存程序將接受指定價格的輸入參數,並將傳回 UnitPrice
欄位小於參數值的所有產品。
若要將新的預存程序新增至專案中,請以滑鼠右鍵按一下 ManagedDatabaseConstructs
專案名稱並選擇新增的預存程序。 將檔案命名為 GetProductsWithPriceLessThan.vb
。 正如我們在步驟 3 中看到的,這將建立一個新的 Visual Basic Partial
類別文件,並將名為 GetProductsWithPriceLessThan
的方法放置在 StoredProcedures
類別中。
更新 GetProductsWithPriceLessThan
方法的定義,使其接受名為 SqlMoney
的輸入參數,並編寫程式碼來執行 price
並傳回查詢結果:
<Microsoft.SqlServer.Server.SqlProcedure()> _
Public Shared Sub GetProductsWithPriceLessThan(ByVal price As SqlMoney)
'Create the command
Dim myCommand As New SqlCommand()
myCommand.CommandText = _
"SELECT ProductID, ProductName, SupplierID, CategoryID, " & _
" QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, " & _
" ReorderLevel, Discontinued " & _
"FROM Products " & _
"WHERE UnitPrice < @MaxPrice"
myCommand.Parameters.AddWithValue("@MaxPrice", price)
' Execute the command and send back the results
SqlContext.Pipe.ExecuteAndSend(myCommand)
End Sub
GetProductsWithPriceLessThan
方法的定義和代碼與步驟 3 中建立的 GetDiscontinuedProducts
方法的定義和代碼非常相似。 唯一的差異是 GetProductsWithPriceLessThan
方法接受 (price
) 作為輸入參數,s 查詢包含參數 (SqlCommand
),並且將參數新增至 @MaxPrice
Parameters
集合中並指派 price
變數的值 SqlCommand
。
新增此程式碼後,重新部署 SQL Server 專案。 接下來,返回 SQL Server Management Studio 並刷新預存程序資料夾。 您應該會看到一個新條目 GetProductsWithPriceLessThan
。 在查詢視窗中,輸入並執行命令 exec GetProductsWithPriceLessThan 25
,這將列出所有低於 25 美元的產品,如圖 14 所示。
圖 14:顯示 25 美元以下的產品 (點擊看大圖)
步驟 6:從資料存取層呼叫受控預存程序
此時,我們已將 GetProductsWithPriceLessThan
受控預存程序新增至 GetDiscontinuedProducts
專案中,並將 ManagedDatabaseConstructs
註冊到 Northwind SQL Server 資料庫中。 我們也從 SQL Server Management Studio 呼叫這些受控預存程序 (請參閱圖 13 和 14)。 然而,為了讓我們的 ASP.NET 應用程式使用這些受控預存程序,我們需要將它們新增至體系結構中的資料存取層和業務邏輯層。 在此步驟中,我們將向類型化資料集 ProductsTableAdapter
中新增兩個新方法,該方法最初是在為 NorthwindWithSprocs
類型化資料集的 TableAdapters 建立新預存程序教學課程中建立的。 在步驟7中我們將在 BLL 中加入對應的方法。
在 Visual Studio 中開啟 NorthwindWithSprocs
類型化資料集,然後先向命名的 ProductsTableAdapter
GetDiscontinuedProducts
。 若要將新方法新增至 TableAdapter,請在設計器中以滑鼠右鍵按一下 TableAdapter 的名稱,然後從上下文功能表中選擇「新增查詢」選項。
注意
由於我們將 Northwind 資料庫從 App_Data
資料夾移至 SQL Server 2005 Express Edition 資料庫執行個體,因此必須更新 Web.config 中的相應連接字串以反映此變更。 在步驟 2 中,我們討論了更新 Web.config
中的 NORTHWNDConnectionString
值。 如果您忘記進行此更新,那麼您將看到錯誤訊息「無法新增查詢」。 嘗試為 TableAdapter 新增方法時,無法在對話方塊中找到 Web.config
物件的連線 NORTHWNDConnectionString
。 若要解決此錯誤,請按一下確定,然後前往步驟 2 中 Web.config
所述並更新 NORTHWNDConnectionString
值。 然後嘗試將該方法重新新增至 TableAdapter。 這次應該可以正常工作,不會有錯誤。
新增方法會啟動 TableAdapter 查詢設定精靈,我們在過去的教學課程中多次使用過該精靈。 第一步要求我們指定 TableAdapter 應如何存取資料庫:透過臨機操作 SQL 陳述式或透過新的或現有的預存程序。 由於我們已經建立並向資料庫註冊了 GetDiscontinuedProducts
受控預存程序,因此選擇使用現有預存程序選項並按一下下一步。
圖 15:選擇「使用現有預存程序」選項 (點選查看大圖)
下一個畫面提示我們該方法將呼叫的預存程序。 從下拉清單中選擇 GetDiscontinuedProducts
受控預存程序,然後按一下「下一步」。
圖 16:選擇 GetDiscontinuedProducts
受控預存程序 (點選查看大圖)
然後要求我們指定預存程序是否傳回行、單一值或不傳回任何內容。 由於 GetDiscontinuedProducts
返回停產產品行集,因此選擇第一個選項 (表格資料)並按下一步。
圖 17:選擇表格資料選項 (點選查看全尺寸影像)
最後的嚮導畫面可讓我們指定所使用的資料存取模式以及產生的方法的名稱。 選取兩個核取方塊並將方法命名為 FillByDiscontinued
和 GetDiscontinuedProducts
。 按一下 [完成] 以完成程序。
圖 18:命名方法 FillByDiscontinued
和 GetDiscontinuedProducts
(點擊查看全尺寸影像)
重複這些步驟,為 ProductsTableAdapter
受控預存程序建立 GetProductsWithPriceLessThan
名為 FillByPriceLessThan
和 GetProductsWithPriceLessThan
的方法。
ProductsTableAdapter
圖 19GetDiscontinuedProducts
顯示了將方法新增至 GetProductsWithPriceLessThan
受控預存程序後的資料集設計器的螢幕截圖。
圖 19:ProductsTableAdapter
包含此步驟中新增的新方法 (按一下查看大圖)
第七步:在業務邏輯層加入對應的方法
現在我們已經更新了資料存取層以包含呼叫步驟 4 和 5 中新增的受控預存程序的方法,我們需要向業務邏輯層添加相應的方法。 在 ProductsBLLWithSprocs
類別中加入以下兩個方法:
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetDiscontinuedProducts() As NorthwindWithSprocs.ProductsDataTable
Return Adapter.GetDiscontinuedProducts()
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsWithPriceLessThan(ByVal priceLessThan As Decimal) _
As NorthwindWithSprocs.ProductsDataTable
Return Adapter.GetProductsWithPriceLessThan(priceLessThan)
End Function
這兩種方法都只是呼叫對應的 DAL 方法並傳回 ProductsDataTable
執行個體。 每個方法上方的 DataObjectMethodAttribute
標記使這些方法包含在 ObjectDataSource 配置資料來源精靈的 SELECT 標籤的下拉清單中。
步驟 8:從表示層呼叫受控預存程序
隨著業務邏輯和資料存取層的增強,包括對呼叫 GetDiscontinuedProducts
和 GetProductsWithPriceLessThan
受控預存程序的支持,我們現在可以透過 ASP.NET 頁面顯示這些預存程序結果。
打開 AdvancedDAL
資料夾中的 ManagedFunctionsAndSprocs.aspx
頁面,然後從工具箱中將 GridView 拖曳到設計器上。 將 GridView 的 ID
屬性設為 DiscontinuedProducts
,並從其智慧標記將其綁定到名為 的新 ObjectDataSource DiscontinuedProductsDataSource
。 配置 ObjectDataSource 以從 ProductsBLLWithSprocs
類別的 GetDiscontinuedProducts
方法中提取其資料。
圖 20:將 ObjectDataSource 設定為使用 ProductsBLLWithSprocs
類別 (按一下以檢視完整大小的影像)
圖 21:從 SELECT 標籤的下拉清單中選擇 GetDiscontinuedProducts
方法 (按一下查看全尺寸影像)
由於此網格將僅用於顯示產品資訊,因此請將更新、插入和刪除選項卡中的下拉列表設為 (無),然後按完成。
完成精靈後,Visual Studio 將自動為 ProductsDataTable
。 花點時間刪除除 ProductName
和 Discontinued
之外的所有這些欄位,此時您的 GridView 和 ObjectDataSource 聲明性標記應類似於以下內容:
<asp:GridView ID="DiscontinuedProducts" runat="server"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="DiscontinuedProductsDataSource">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="ProductName"
SortExpression="ProductName" />
<asp:CheckBoxField DataField="Discontinued"
HeaderText="Discontinued"
SortExpression="Discontinued" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="DiscontinuedProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetDiscontinuedProducts" TypeName="ProductsBLLWithSprocs">
</asp:ObjectDataSource>
請花點時間透過瀏覽器查看此頁面。 當頁面被存取時,ObjectDataSource 呼叫 ProductsBLLWithSprocs
類別的 GetDiscontinuedProducts
方法。 正如我們在步驟 7 中看到的,該方法向下呼叫 DAL ProductsDataTable
類別的 GetDiscontinuedProducts
方法,該方法呼叫 GetDiscontinuedProducts
預存程序。 此預存程序是受控預存程序,執行我們在步驟 3 中建立的程式碼,傳回停產的產品。
受控儲存程序傳回的結果由 ProductsDataTable
DAL 打包成 a,然後回到 BLL,BLL 再將它們傳回表示層,在表示層中將它們綁定到 GridView並顯示。 正如預期的那樣,網格列出了那些已停產的產品。
圖 22:停產產品清單 (點擊看大圖)
為了進一步練習,請在頁面上新增一個 TextBox 和另一個 GridView。 透過呼叫 ProductsBLLWithSprocs
類別的 GetProductsWithPriceLessThan
方法,讓此 GridView 顯示少於在 TextBox 中輸入的金額的產品。
步驟 9:建立並呼叫 T-SQL UDF
使用者定義函數 (UDF) 是非常模仿程式語言中函數語意的資料庫物件。 與 Visual Basic 中的函數類似,UDF 可以包含可變數量的輸入參數並傳回特定類型的值。 UDF 可以傳回標量資料 (字串、整數等) 或表格資料。 讓我們快速瀏覽一下這兩種類型的 UDF,從傳回標量資料類型的 UDF 開始。
以下 UDF 計算特定產品的庫存估計值。 它透過接受三個輸入參數 (特定產品的 UnitPrice
、UnitsInStock
和 Discontinued
值) 並傳回類型的值來實現此目的 money
。 它透過乘以 UnitsInStock
來計算 UnitPrice
庫存的估計價值。 對於停產的產品,該值減半。
CREATE FUNCTION udf_ComputeInventoryValue
(
@UnitPrice money,
@UnitsInStock smallint,
@Discontinued bit
)
RETURNS money
AS
BEGIN
DECLARE @Value decimal
SET @Value = ISNULL(@UnitPrice, 0) * ISNULL(@UnitsInStock, 0)
IF @Discontinued = 1
SET @Value = @Value * 0.5
RETURN @Value
END
一旦將此 UDF 新增至資料庫中,就可以透過 Management Studio 找到它,方法是依序展開「可程式性」資料夾、「函數」和「標量值函數」。 它可以在 SELECT
查詢中使用,如下所示:
SELECT ProductID, ProductName, dbo.udf_ComputeInventoryValue
(UnitPrice, UnitsInStock, Discontinued) as InventoryValue
FROM Products
ORDER BY InventoryValue DESC
我已將 udf_ComputeInventoryValue
UDF 新增至 Northwind 資料庫;圖 23 顯示了透過 Management Studio 查看上述 SELECT
查詢的輸出。 另請注意,UDF 列在物件資源管理器中的標量值函數資料夾下。
圖 23:列出了每種產品的庫存價值 (點擊看大圖)
UDF 也可以傳回表格資料。 例如,我們可以建立一個傳回屬於特定類別的產品的 UDF:
CREATE FUNCTION dbo.udf_GetProductsByCategoryID
(
@CategoryID int
)
RETURNS TABLE
AS
RETURN
(
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued
FROM Products
WHERE CategoryID = @CategoryID
)
udf_GetProductsByCategoryID
UDF 接受 @CategoryID
輸入參數並傳回指定 SELECT
查詢的結果。 建立後,可以在 SELECT
查詢的 FROM
(或 JOIN
) 子句中引用此 UDF。 以下範例將傳回每種飲料的 ProductID
、ProductName
和 CategoryID
值。
SELECT ProductID, ProductName, CategoryID
FROM dbo.udf_GetProductsByCategoryID(1)
我已將 udf_GetProductsByCategoryID
UDF 新增至 Northwind 資料庫;圖 24 顯示了透過 Management Studio 查看上述 SELECT
查詢的輸出。 傳回表格資料的 UDF 可以在物件資源管理器的表值函數資料夾中找到。
圖 24:每種飲料列出 ProductID
、ProductName
、CategoryID
和 (點擊看大圖)
注意
有關建立和使用 UDF 的更多資訊,請查看使用者定義函數簡介。 另請查看使用者定義函數的優點和缺點。
步驟 10:建立受控 UDF
上面範例中建立的 udf_ComputeInventoryValue
和 udf_GetProductsByCategoryID
UDF 是 T-SQL 資料庫物件。 SQL Server 2005 也支援受控 UDF,它可以像步驟 3 和 5 中的受控預存程序一樣新增到 ManagedDatabaseConstructs
專案中。 對於此步驟,讓我們在受控程式碼中實作 udf_ComputeInventoryValue
UDF。
若要將受控 UDF 新增至 ManagedDatabaseConstructs
專案中,請在「解決方案資源管理器」中以滑鼠右鍵按一下專案名稱,然後選擇「新增專案」。 從新增專案對話方塊中選擇使用者定義範本,並將新的 UDF 檔案命名為 udf_ComputeInventoryValue_Managed.vb
。
圖 25:新增新的受控 UDF ManagedDatabaseConstructs
(點擊查看大圖)
使用者定義函數範本建立一個 Partial
類別,該類別的名稱 UserDefinedFunctions
與類別文件的名稱 (在本例中 udf_ComputeInventoryValue_Managed
) 相同。 此方法使用 SqlFunction
屬性進行修飾,該屬性將方法標記為受控 UDF。
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
Partial Public Class UserDefinedFunctions
<Microsoft.SqlServer.Server.SqlFunction()> _
Public Shared Function udf_ComputeInventoryValue_Managed() As SqlString
' Add your code here
Return New SqlString("Hello")
End Function
End Class
udf_ComputeInventoryValue
方法目前傳回一個 SqlString
物件,不接受任何 輸入參數。 我們需要更新方法定義,以便它接受三個輸入參數 UnitPrice
- UnitsInStock
、Discontinued
和 SqlMoney
並傳回一個物件。 庫存值的計算邏輯與 T-SQL udf_ComputeInventoryValue
UDF 中的邏輯相同。
<Microsoft.SqlServer.Server.SqlFunction()> _
Public Shared Function udf_ComputeInventoryValue_Managed _
(UnitPrice As SqlMoney, UnitsInStock As SqlInt16, Discontinued As SqlBoolean) _
As SqlMoney
Dim inventoryValue As SqlMoney = 0
If Not UnitPrice.IsNull AndAlso Not UnitsInStock.IsNull Then
inventoryValue = UnitPrice * UnitsInStock
If Discontinued = True Then
inventoryValue = inventoryValue * New SqlMoney(0.5)
End If
End If
Return inventoryValue
End Function
請注意,UDF 方法的輸入參數是 SqlMoney
對應的 SQL 類型:UnitPrice
欄位、SqlInt16
和 UnitsInStock
。SqlBoolean
Discontinued
這些資料類型反映了 Products
表中定義的 UnitPrice
類型:money
列的類型為 UnitsInStock
、列的類型為 smallint
、Discontinued
列的類型為 bit
。
程式碼首先建立一個名為 SqlMoney
的執行個體,並為 inventoryValue
指派值 0。 Products
表允許在 NULL
和 UnitsInPrice
列中使用資料庫值 UnitsInStock
。 因此,我們需要先檢查這些值是否包含 NULL
,這是透過物件的 SqlMoney
IsNull
屬性來完成的。 如果 UnitPrice
和 UnitsInStock
都包含非值,那麼我們將 NULL
計算為兩者的乘積 inventoryValue
。 然後,如果 Discontinued
為真,那麼我們將值減半。
注意
SqlMoney
物件只允許兩個 SqlMoney
執行個體相乘。 它不允許 SqlMoney
執行個體乘以文字浮點數。 因此,為了減半,inventoryValue
我們將其乘以一個值為 0.5 的新 SqlMoney
執行個體。
步驟 11:部署受控 UDF
現在受控 UDF 已經建立完成,我們可以將其部署到 Northwind 資料庫。 正如我們在步驟 4 中所看到的,透過在解決方案資源管理器中右鍵點擊專案名稱並從上下文功能表中選擇「部署」選項來部署 SQL Server 專案中的受控物件。
部署專案後,返回 SQL Server Management Studio 並刷新標量值函數資料夾。 現在您應該看到兩個條目:
dbo.udf_ComputeInventoryValue
- 步驟 9 中建立的 T-SQL UDF,以及dbo.udf ComputeInventoryValue_Managed
- 在步驟 10 中建立的剛剛部署的受控 UDF。
若要測試此受控 UDF,請從 Management Studio 執行下列查詢:
SELECT ProductID, ProductName,
dbo.udf_ComputeInventoryValue_Managed(
UnitPrice,
UnitsInStock,
Discontinued
) as InventoryValue
FROM Products
ORDER BY InventoryValue DESC
此命令使用受控 udf ComputeInventoryValue_Managed
UDF 而不是 T-SQL udf_ComputeInventoryValue
UDF,但輸出是相同的。 請返回圖 23 查看 UDF 輸出的螢幕截圖。
第 12 步:偵錯受控資料庫物件
在偵錯儲存程序教學課程中,我們討論了透過 Visual Studio 偵錯 SQL Server 的三個選項:直接資料庫偵錯、應用程式偵錯和從 SQL Server 專案偵錯。 受控資料庫物件無法透過直接資料庫偵錯進行偵錯,但可以從用戶端應用程式和直接從 SQL Server 專案進行偵錯。 然而,為了進行偵錯,SQL Server 2005 資料庫必須允許 SQL/CLR 偵錯。 回想一下,當我們第一次建立 ManagedDatabaseConstructs
專案時,Visual Studio 詢問我們是否要啟用 SQL/CLR 偵錯 (請參閱步驟 2 中的圖 6)。 可以透過在伺服器資源管理器視窗中右鍵點擊資料庫來修改此設定。
圖 26:確保資料庫允許 SQL/CLR 偵錯
想像一下我們想要偵錯 GetProductsWithPriceLessThan
受控預存程序。 我們先在 GetProductsWithPriceLessThan
方法的程式碼中設定一個斷點。
圖 27:在 GetProductsWithPriceLessThan
方法中設定斷點 (點擊看大圖)
讓我們先看看如何偵錯 SQL Server 專案中的受控資料庫物件。 由於我們的解決方案包括兩個專案 - ManagedDatabaseConstructs
SQL Server 專案和我們的網站 - 為了從 SQL Server 專案進行偵錯,我們需要指示 Visual Studio 在開始偵錯時啟動 ManagedDatabaseConstructs
SQL Server 專案。 在「解決方案資源管理器」中以滑鼠右鍵按一下該 ManagedDatabaseConstructs
9 專案,然後從上下文功能表中選擇「設定為啟動專案」選項。
當 ManagedDatabaseConstructs
專案從偵錯器啟動時,它會執行位於 Test Scripts
資料夾中的 Test.sql
檔案中的 SQL 陳述式。 例如,要測試 GetProductsWithPriceLessThan
受控預存程序,請將現有 Test.sql
檔案內容替換為下列陳述式,該陳述式會呼叫傳入 @CategoryID
值 14.95 的 GetProductsWithPriceLessThan
受控預存程序:
exec GetProductsWithPriceLessThan 14.95
將上述腳本輸入到 Test.sql
中後,透過前往「偵錯」功能表並選擇「開始偵錯」或按 F5 或工具列中的綠色播放圖示來開始偵錯。 這將在解決方案中建置專案,將受控資料庫物件部署到 Northwind 資料庫,然後執行 Test.sql
腳本。 此時,將命中斷點,我們可以單步執行 GetProductsWithPriceLessThan
方法,檢查輸入參數的值,等等。
圖 28:GetProductsWithPriceLessThan
方法中的斷點被擊中 (點擊看大圖)
為了讓 SQL 資料庫物件透過用戶端應用程式進行偵錯,資料庫必須設定為支援應用程式偵錯。 右鍵點擊伺服器資源管理器中的資料庫,並確保選取應用程式偵錯選項。 此外,我們需要配置 ASP.NET 應用程式以與 SQL 偵錯器整合並停用連接池。 偵錯預存程序教學課程的步驟 2 詳細討論了這些步驟。
配置 ASP.NET 應用程式和資料庫後,將 ASP.NET 網站設定為啟動專案並開始偵錯。 如果您造訪的頁面呼叫了具有斷點的受控物件之一,則應用程式將停止並將控制權移交給偵錯器,您可以在偵錯器中單步偵錯程式碼,如圖 28 所示。
步驟 13:手動編譯和部署受控資料庫物件
SQL Server 專案可以輕鬆建立、編譯和部署受控資料庫物件。 不幸的是,SQL Server 專案僅在 Visual Studio 的 Professional 和 Team Systems 版本中可用。 如果您使用的是 Visual Web Developer 或 Visual Studio 標準版並希望使用受控資料庫物件,則需要手動建立和部署它們。 這涉及四個步驟:
- 建立一個包含受控資料庫物件的原始程式碼的文件,
- 將物件編譯成程式集,
- 將組件註冊到 SQL Server 2005 資料庫,然後
- 在 SQL Server 中建立一個指向程式集中適當方法的資料庫物件。
為了說明這些任務,讓我們建立一個新的受控預存過程,UnitPrice
會傳回大於指定值的乘積。 在您的電腦上建立一個名為 GetProductsWithPriceGreaterThan.vb
的新文件,並將以下程式碼輸入到該文件中 (您可以使用 Visual Studio、記事本或任何文字編輯器來完成此操作):
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
Partial Public Class StoredProcedures
<Microsoft.SqlServer.Server.SqlProcedure()> _
Public Shared Sub GetProductsWithPriceGreaterThan(ByVal price As SqlMoney)
'Create the command
Dim myCommand As New SqlCommand()
myCommand.CommandText = _
"SELECT ProductID, ProductName, SupplierID, CategoryID, " & _
" QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, " & _
" ReorderLevel, Discontinued " & _
"FROM Products " & _
"WHERE UnitPrice > @MinPrice"
myCommand.Parameters.AddWithValue("@MinPrice", price)
' Execute the command and send back the results
SqlContext.Pipe.ExecuteAndSend(myCommand)
End Sub
End Class
此程式碼與步驟 5 中建立的 GetProductsWithPriceLessThan
方法的程式碼幾乎相同。 唯一的區別是查詢中使用的方法名稱、WHERE
子句和參數名稱。 回到 GetProductsWithPriceLessThan
方法中,WHERE
子句為:WHERE UnitPrice < @MaxPrice
。 在這裡,在 GetProductsWithPriceGreaterThan
中,我們使用:WHERE UnitPrice > @MinPrice
。
我們現在需要將此類編譯成程序集。 從命令列導航到儲存 GetProductsWithPriceGreaterThan.vb
檔案的目錄,然後使用 C# 編譯器 (csc.exe
) 將類別檔案編譯為組件:
vbc.exe /t:library /out:ManuallyCreatedDBObjects.dll GetProductsWithPriceGreaterThan.vb
如果包含 bc.exe
的資料夾不在系統 PATH
中,則必須完全引用其路徑 %WINDOWS%\Microsoft.NET\Framework\version\
,如下所示:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\vbc.exe /t:library /out:ManuallyCreatedDBObjects.dll GetProductsWithPriceGreaterThan.vb
圖 29:GetProductsWithPriceGreaterThan.vb
編譯成組件 (點擊查看全尺寸圖片)
此 /t
標誌指定應將 Visual Basic 類別檔案編譯為 DLL (而不是可執行檔)。 此 /out
標誌指定產生的組件的名稱。
注意
您可以使用 Visual Basic Express Edition 或在 Visual Studio Standard Edition 中建立單獨的 GetProductsWithPriceGreaterThan.vb
類別庫專案,而不是從命令列編譯類別檔案。 ren Jacob Lauritsen 慷慨地提供了一個 Visual Express Edition 專案,其中包含 GetProductsWithPriceGreaterThan
預存程序的程式碼以及在步驟 3、5 和 10 中建立的兩個受控預存程序和 UDF。 S ren 的專案還包括新增對應資料庫物件所需的 T-SQL 命令。
將程式碼編譯成組件後,我們就可以在 SQL Server 2005 資料庫中註冊該組件了。 這可以透過 T-SQL、使用命令 CREATE ASSEMBLY
或透過 SQL Server Management Studio 來執行。 讓我們重點關注 Management Studio 的使用。
從 Management Studio 中,展開 Northwind 資料庫中的 Programmability 資料夾。 它的子資料夾之一是 Assemblies。 若要手動將新組件新增至資料庫,請以滑鼠右鍵按一下組件資料夾,然後從上下文功能表中選擇新建組件。 這將顯示「新組裝」對話框 (請參閱圖 30)。 按一下「瀏覽」按鈕,選擇我們剛剛編譯的 ManuallyCreatedDBObjects.dll
組件,然後按一下「確定」將組件新增至資料庫。 您不應在物件資源管理器中看到 ManuallyCreatedDBObjects.dll
組件。
圖 30:將 ManuallyCreatedDBObjects.dll
組件新增至資料庫 (按一下查看全尺寸影像)
圖 31:在物件資源管理器中列出 ManuallyCreatedDBObjects.dll
雖然我們已將組件新增至 Northwind 資料庫,但尚未將預存程序與程序集中的 GetProductsWithPriceGreaterThan
方法關聯起來。 為此,請開啟一個新的查詢視窗並執行以下腳本:
CREATE PROCEDURE [dbo].[GetProductsWithPriceGreaterThan]
(
@price money
)
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [ManuallyCreatedDBObjects].[StoredProcedures].[GetProductsWithPriceGreaterThan]
GO
這將在 Northwind 資料庫中建立一個名為 GetProductsWithPriceGreaterThan
的新預存程序,並將其與受控方法 GetProductsWithPriceGreaterThan
(位於 StoredProcedures
類別中,位於程序集中 ManuallyCreatedDBObjects
) 相關聯。
執行上述腳本後,刷新物件資源管理器中的預存程序資料夾。 您應該會看到一個新的預存程序條目 - GetProductsWithPriceGreaterThan
- 旁邊有一個鎖定圖示。 若要測試此預存程序,請在查詢視窗中輸入並執行下列腳本:
exec GetProductsWithPriceGreaterThan 24.95
如圖 32 所示,上述命令顯示價格 UnitPrice
超過 24.95 美元的產品的資訊。
圖 32:ManuallyCreatedDBObjects.dll
物件資源管理器中列出 (按一下查看大圖)
摘要
Microsoft SQL Server 2005 提供與公共語言執行時間 (CLR) 的整合,允許使用受控程式碼建立資料庫物件。 以前,這些資料庫物件只能使用 T-SQL 建立,但現在我們可以使用 .NET 程式語言 (如 Visual Basic) 來建立這些物件。 在本教學課程中,我們建立了兩個受控預存程序和一個受控使用者定義函數。
Visual Studio 的 SQL Server 專案類型有助於建立、編譯和部署受控資料庫物件。 此外,它還提供豐富的偵錯支援。 但是,SQL Server 專案類型僅在 Visual Studio 的 Professional 和 Team Systems 版本中可用。 對於使用 Visual Web Developer 或 Visual Studio 標準版的使用者,必須手動執行建立、編譯和部署步驟,如我們在步驟 13 中看到的那樣。
祝您程式設計愉快!
深入閱讀
有關本教學課程中討論的主題的更多資訊,請參閱以下資源:
- 使用者定義函數的優點和缺點
- 在受控程式碼中建立 SQL Server 2005 物件
- 如何:建立並執行 CLR SQL Server 預存程序
- 如何:建立並執行 CLR SQL Server 使用者定義函數
- 如何:編輯腳本以運行 SQL 物件
Test.sql
- 使用者定義函數簡介
- 受控程式碼和 SQL Server 2005 (影片)
- Transact-SQL 參考
- 演練:在受控程式碼中建立預存程序
關於作者
Scott Mitchell,七本 ASP/ASP.NET 書籍的作者和 4GuysFromRolla.com 創始人,自 1998 年以來便開始使用 Microsoft Web 技術。 Scott 擔任獨立顧問、講師和作家。 他的新書是 Sams Teach Yourself ASP.NET 2.0 in 24 Hours。 您可以透過 mitchell@4GuysFromRolla.com 或他的部落格 (可以在 http://ScottOnWriting.NET 找到) 與他聯繫。
特別感謝
本教學課程系列已經過許多熱心的檢閱者檢閱。 本教學課程的主要審閱者是 S ren Jacob Lauritsen。 除了審閱本文之外,S ren 還建立了本文下載中包含的 Visual C# Express Edition 專案,用於手動編譯受控資料庫物件。 有興趣檢閱我即將推出的 MSDN 文章嗎? 如果有,請發信到 mitchell@4GuysFromRolla.com 。