使用C#創建SQL Server的存儲過程

通常,開發人員使用的是T-SQL來創建SQL Server的存儲過程、函數和觸發器。 而現在的SQL Server 2005已經完全支持.NET通用語言運行時(CLR)了。 這就意味着,你可以使用.NET的語言,如C#、VB.NET之類的來開發SQL Server的存儲過程、函數和觸發器。 SQL Server 和 CLR 的集成給我們帶來了n多好處,如實時編譯、類型安全、增強的安全性以及增強的編程模型等。 本文中,我將向大家演示如何使用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的會員。

發佈了28 篇原創文章 · 獲贊 3 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章