轉自:http://blog.csdn.net/hillspring/article/details/2304900
通常,開發人員使用的是T-SQL來建立SQL Server的預存程序、函數和觸發器。 而現在的SQL Server 2005已經完全支援.NET通用語言運行時(CLR)了。 這就意味著,你可以使用.NET的語言,如C#、VB.NET之類的來開發SQL Server的預存程序、函數和觸發器。 SQL Server 和 CLR 的整合給我們帶來了n多好處,如Just-In-Time 編譯、型別安全、增強的安全性以及增強編程模型等。 本文中,我將向大家示範如何使用C#建立SQL Server的預存程序。
背景
我們在使用SQL Server預存程序時,最常做的工作就是從資料庫中讀取或儲存資料。 其常用應用如下:
◆執行一些簡單的邏輯,沒有任何傳回值。 也沒有輸出參數。
◆執行一些邏輯,並通過一個或更多的輸出參數返回結果。
◆執行一些邏輯,並返回從表中讀取的一條或多條記錄。
◆執行一些邏輯,並返回一行或多行記錄。 這些記錄不是從表中讀取的,而是你自訂的一些資料行。
為了示範如何用C#開發出這幾種應用的SQL Server預存程序,我將一個一個地舉出樣本。
啟用CLR整合
在你開始用C#寫預存程序之前,必須要啟用你的SQL Server的CLR整合特性。 預設情況它是不啟用的。 開啟你的SQL Server Management Studio並執行如下指令碼。
sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO
這裡,我們執行了系統預存程序“sp_configure”,為其提供的兩個參數分別為:“clr enabled”和“1”。如果要停用CLR整合的話也是執行這個預存程序,只不過第二個參數要變為“0”而已。另外,為了使新的設定產生效果,不要忘記調用“RECONFIGURE”。
SQL Server項目
現在開啟Visual Studio,並從檔案菜單中選擇“建立項目”。 在“建立項目”對話方塊中選擇“Visual C#”下的“Database”。 然後選擇“SQL Server項目”模板。
起好項目名稱後就單擊“確定”按鈕。
很快,你所建立的項目就要求你選擇一個SQL Server資料庫。
按照提示一步一步地做就好了,就算你選擇了取消,也可以在“項目”–“屬性”對話方塊中再一次選擇資料庫。 舉個例子,假如你的電腦上有一個“北風貿易”資料庫,那麼就在“建立資料庫引用”對話方塊中選中它,然後單擊“確定”按鈕。 之後,SQL Server項目在部署的時候就會將我們開發的預存程序寫入這個資料庫(繼續往後看你就清楚是怎麼回事了)。
接下來,按右鍵你建立的這個項目,選擇“添加”-“預存程序”。 然後將會出現如所示的對話方塊:
選擇“預存程序”模板,並起一個合適的名字,然後單擊“添加”按鈕。
添加完後你就會發現,實際上這是建立了一個已經匯入了需要用到的命名空間的類。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
注意一下加粗顯示的命名空間(譯者註:後兩個using)。 System.Data.SqlTypes命名空間包含了很多不同的類型,它們可以用來代替SQL Server的資料類型。 Microsoft.SqlServer.Server命名空間下的類負責SQL Server的CLR整合。
說明:本程式操作“北風貿易”資料庫,“訂貨主檔”表,表內含有的欄位依次是:訂單號碼(int),送貨城市(nvarchar(15)) …其他欄位省略。
沒有傳回值的預存程序
在這一節中,我們將會看到如何寫一個執行了一些邏輯,但是卻沒有任何傳回值和輸出參數的預存程序。 在這個例子裡,我們將建立一個名為“ChangeCityName”的預存程序,它用來修改“訂貨主檔”表中“送貨城市”欄位的值。 這個預存程序需要兩個參數 –ID(需要更改“送貨城市”的“訂單號碼”ID)和City(新的“送貨城市”)。 “ChangeCityName”預存程序完成後的代碼如下:(Procedure1)
[Microsoft.SqlServer.Server.SqlProcedure]
public static void ChangeCityName(SqlInt32 ID, SqlString City)
{
// 在此處放置代碼
SqlConnection con = new SqlConnection("context connection = true");
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "update 訂貨主檔 set 送貨城市=@City where 訂單號碼=@ID";
SqlParameter p1 = new SqlParameter("@City", City);
SqlParameter p2 = new SqlParameter("@ID", ID);
cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
int row = cmd.ExecuteNonQuery();
con.Close();
SqlContext.Pipe.Send(row.ToString());
}
};
仔細看一下這個ChangeCityName ()方法。 它是一個靜態方法並且沒有傳回值(void)。 它需要兩個名為ID和City的參數。 請注意這兩個參數的資料類型是SqlInt32和SqlString。 SqlString可以用來代替SQL Server中的nvarchar資料類型。 這個方法用了一個[SqlProcedure]屬性來修飾。 該屬性用於標記ChangeCityName()方法是一個SQL Server預存程序。
在方法內我們建立了一個SqlConnection對象,並設定其連接字串為“context connection = true”。 “上下文串連”可以讓你使用當前登入到資料庫的使用者作為你的登入資料庫的驗證資訊。 本例中,ChangeCityName()方法將會轉換為預存程序,然後儲存到“北風貿易”資料庫裡。 所以在這裡的“上下文串連”指的就是“北風貿易”資料庫。 這樣你就不需要再寫任何關於登入資料庫的驗證資訊了。
接下來是開啟資料庫連接。 然後通過設定SqlCommand對象的Connection和CommandText屬性,讓其執行更新操作。 同時,我們還需要設定兩個參數。 這樣通過調用ExecuteNonQuery()方法就可以執行更新操作了。 再接下來就是關閉串連。
最後,將ExecuteNonQuery()方法的傳回值發送到用戶端。 當然你也可以不做這一步。 現在我們來瞭解一下SqlContext類的使用。 SqlContext類用於在服務端和用戶端之間傳遞處理結果。 本例使用了Send()方法發送一個字串返回給調用者。
返回從表中讀取的一條或多條記錄的預存程序
我們在使用預存程序時,經常會SELECT一條或多條記錄。 你可以採用兩種方法來建立這樣的預存程序。
首先我們建立一個名為GetAllCustomers()的方法,代碼如下:(Procedure2.1)
[Microsoft.SqlServer.Server.SqlProcedure]
public static void GetAllInfo()
{
SqlConnection con = new SqlConnection("context connection = true");
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "Select * from 訂貨主檔";
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
SqlContext.Pipe.Send(reader);
reader.Close();
con.Close();
}
這個GetAllInfo ()方法用了一個[SqlProcedure]屬性來修飾。 在方法內建立一個SqlConnection和一個SqlCommand對象。 然後使用ExecuteReader()方法來執行SELECT語句。 接下來用Send()方法將取得的SqlDataReader資料發送到用戶端。 最後就是關閉SqlDataReader和SqlConnection。 在這種方法中,是我們自己建立的SqlDataReader。
其實,我們也可以把這個任務交給SqlContext類去完成,代碼如下:(Procedure2.2)
[Microsoft.SqlServer.Server.SqlProcedure]
public static void GetAllInfoByID(SqlInt32 ID)
{
SqlConnection con = new SqlConnection("context connection=true");
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "select * from 訂貨主檔 where 訂單號碼=@p1";
SqlParameter p1 = new SqlParameter("@p1", ID);
cmd.Parameters.Add(p1);
SqlContext.Pipe.ExecuteAndSend(cmd);
con.Close();
}
GetAllInfoByID ()方法需要一個參數 –ID,它將從“訂貨主檔”表中返回某行的記錄。 這個方法內的代碼,除了ExecuteAndSend()方法外,你應該都已經比較熟悉了。 ExecuteAndSend()方法接收一個SqlCommand對象作為參數,執行它就會返回資料集給用戶端。
有輸出參數的預存程序
我們在使用預存程序時,經常會通過輸出參數返回一個經過計算的值。 所以,現在讓我們來看一看如何建立具有一個或多個輸出參數的預存程序。(Procedure3)
[Microsoft.SqlServer.Server.SqlProcedure]
public static void GetCityName(SqlInt32 ID, out SqlString City)
{
SqlConnection con = new SqlConnection("context connection=true");
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "Select 送貨城市 from 訂貨主檔 where 訂單號碼=@ID";
SqlParameter id = new SqlParameter("@ID", ID);
cmd.Parameters.Add(id);
object obj = cmd.ExecuteScalar();
con.Close();
City = obj.ToString();
}
這是一個名為GetCityName ()的方法,它需要兩個參數。 第一個參數是ID,它是一個輸入參數;第二個參數是City,它是一個輸出參數(用關鍵字out來指明)。 這兩個參數是SqlInt32和SqlString類型的。 GetCityName ()方法會接收一個ID參數,然後返回City(作為輸出參數)。
該方法內的代碼首先設定了SqlConnection和SqlCommand對象。 然後,使用ExecuteScalar()方法來執行SELECT語句。 ExecuteScalar()方法返回的值是一個object類型,它其實就是公司名稱。 最後將輸出參數City設定為這個值。
返回一行或多行自訂資料的預存程序
我們在使用預存程序時,更多的還是從某些表中讀取資料。 但是,某些情況下我們需要的資料可能不在任何錶裡。 例如,你可能會基於某些計算來產生一個資料表格。 因為它的資料不是從表中獲得的,所以上面的方法就不在適用了。 幸運的是,SQL Server的CLR整合特性給我們提供了一個解決這個問題的方法。 請看如下代碼:(Procedure4.1)
[Microsoft.SqlServer.Server.SqlProcedure]
public static void GetRow()
{
SqlMetaData[] metadata = new SqlMetaData[2];
metadata[0] = new SqlMetaData("ID", SqlDbType.Int);
metadata[1] = new SqlMetaData("City", SqlDbType.NVarChar, 15);
SqlDataRecord record = new SqlDataRecord(metadata);
record.SetInt32(0, 10248);
record.SetString(1, "北京市");
SqlContext.Pipe.Send(record);
}
GetRow()方法會返回一條記錄並發送給用戶端。 這個方法首先聲明了一個SqlMetaData對象。 當你要用到自訂欄的時候,就可以使用這個SqlMetaData類。 在我們的樣本中,建立了兩個類型為NVarChar,長度為15的列。然後建立了一個SqlDataRecord對象。 SqlDataRecord類可以用來表示一個自訂行。 它的建構函式需要一個SqlMetaData數組作為參數。 SqlDataRecord對象的SetString()方法用來設定列的值。 另外,還有許多不同的類似SetString()這樣的方法,可以用來處理不同的資料類型。 最後,調用Send()方法將SqlDataRecord對象發送到用戶端。
在上面的樣本中,我們只返回了一行資料給調用者。 那麼,如果要返回多行呢? 請看下面的代碼:(Procedure4.2)
[Microsoft.SqlServer.Server.SqlProcedure]
public static void GetMultipleRow()
{
SqlMetaData[] metadata = new SqlMetaData[2];
metadata[0] = new SqlMetaData("ID", SqlDbType.Int);
metadata[1] = new SqlMetaData("City", SqlDbType.NVarChar, 15);
SqlDataRecord record = new SqlDataRecord(metadata);
SqlContext.Pipe.SendResultsStart(record);
record.SetInt32(0, 10249);
record.SetString(1, "天津市");
SqlContext.Pipe.SendResultsRow(record);
record.SetInt32(0, 10250);
record.SetString(1, "天津市");
SqlContext.Pipe.SendResultsRow(record);
SqlContext.Pipe.SendResultsEnd();
}
GetMultipleRow()方法將會返回多個SqlDataRecord對象到用戶端。 接下來建立自訂欄和設定列的值都和之前的例子一樣。 但是,我們使用的是SendResutlsStart()方法來傳輸資料。 SendResultsRow()方法也是發送一個SqlDataRecord對象到用戶端,但是我們可以多次調用它,從而做到發送多條記錄。 最後,調用SendResultsEnd()方法用來標記已經完成資料轉送操作。
我們已經開發完了預存程序。 現在就可以將這個項目編譯為一個程式集(.DLL)。 但是我們的工作並沒有到此結束。 我們還需要部署這個程式集和預存程序到SQL Server資料庫。 有兩種方法可以完成這個工作 – 手動和自動。 手動方法是使用T-SQL語句註冊你的程式集,並將預存程序部署到SQL Server資料庫中。 在本例中,我將使用自動的方法來部署預存程序到SQL Server資料庫。
按右鍵你的項目,然後在菜單中選擇“部署”選項。
如此就會自動地完成註冊程式集和部署預存程序的工作。 注意,只有在你建立項目時添加了資料庫引用的時候,才會出現“部署”選項。 如果因為某些原因你沒能添加資料庫引用,那麼你可以通過項目屬性對話方塊來設定它。
如果你在SQL Server Management Studio查看Northwind資料庫的話,那麼就應該可以看到和相似的結果。
注意,在預存程序節點下出現了我們建立的所有方法(有“鎖”表徵圖的),並且在程式集節點下出現了我們的程式集。
就是這些東西,很簡單吧。 現在你就可以在你的程式中調用這些預存程序了。你也可以在SQL Server Management Studio中來測試它們。
作者簡介:Bipin Joshi是DotNetBips.com的管理員。他是http://www.binaryintellect.com/的發起人,這個公司提供.NET framwork的培訓和諮詢服務。他在印度孟買為開發人員提供培訓。他也是微軟的MVP(ASP.Net)和ASPInsiders的會員。