在SQL Server 2008 R2上觸發 trigger 時呼叫C#(CLR)程式
trigger是當一個table有出現insert、update、delete等動作時會額外觸發的行為,
例如可以用在當使用者被刪除時,可以設定trigger讓他一併刪除相關的資料,
優點是很方便,缺點是當trigger太多的時候,互相干擾之下會非常複雜!
這邊要說的是如何觸發trigger時呼叫C#(CLR)的程式,至於程式內容就不會多加著墨。
程式碼就先參考http://msdn.microsoft.com/en-us/library/938d9dz2(VS.80).aspx [注1]
我就從VS2010的觀點來做說明
先開一個Database Project
當新增這個專案之後,會要你設定連線的SQL,有的話最好就直接設定了,不然之後也可以到屬性頁面改。
而你用來連線的帳號必須對你的目標table有CREATE ASSEMBLY的權限。
空白的專案會長這樣
這裡要特別注意,SQL 2008 r2 只支援.net3.5和之前的版本,
所以記得到專案屬性頁面修改版本為.net3.5。(因為我新增專案的時候忘記改了)
專案底下新增一個Trigger類型的類別
他會將必要的參考加進去,預設的內容如下
1: public partial class Triggers {
2: // Enter existing table or view for the target and uncomment the attribute line
3: // [Microsoft.SqlServer.Server.SqlTrigger (Name="Trigger1", Target="Table1", Event="FOR UPDATE")]
4: public static void Trigger1() {
5: // Replace with your own code
6: SqlContext.Pipe.Send("Trigger FIRED");
7: }
8: }
這邊注意第三行被註解掉的內容
這要寫在function之前,用來告訴SQL觸發哪種類型的trigger,
範例程式碼是insert,所以我們將註解拿掉,並改成insert,接著把範例程式碼複製過來,
1: [SqlTrigger(Name="UserNameAudit", Target="Users", Event="FOR INSERT")]
2: public static void UserNameAudit()
3: {
4: SqlTriggerContext triggContext = SqlContext.TriggerContext;
5: SqlParameter userName = new SqlParameter("@username", System.Data.SqlDbType.NVarChar);
6:
7: if (triggContext.TriggerAction == TriggerAction.Insert)
8: {
9: using (SqlConnection conn = new SqlConnection("context connection=true"))
10: {
11: conn.Open();
12: SqlCommand sqlComm = new SqlCommand();
13: SqlPipe sqlP = SqlContext.Pipe;
14:
15: sqlComm.Connection = conn;
16: sqlComm.CommandText = "SELECT UserName from INSERTED";
17:
18: userName.Value = sqlComm.ExecuteScalar().ToString();
19:
20: if (IsEMailAddress(userName.ToString()))
21: {
22: sqlComm.CommandText = "INSERT UsersAudit(UserName) VALUES(userName)";
23: sqlP.Send(sqlComm.CommandText);
24: sqlP.ExecuteAndSend(sqlComm);
25: }
26: }
27: }
28: }
29:
30:
31: public static bool IsEMailAddress(string s)
32: {
33: return Regex.IsMatch(s, "^([\\w-]+\\.)*?[\\w-]+@[\\w-]+\\.([\\w-]+\\.)*?[\\w]+$");
34: }
SqlTrigger有幾個屬性要設定,Name是此trigger的名稱,隨你取,
Target是要對應的Table,Event是發生此事件才要呼叫。
如果有多個動作都要觸發的話,Event寫成"FOR INSERT UPDATE DELETE"就可以了。
到此為止是VS端的事情,接著要說明SQL端,
為了建立測試環境,先建立一個DB叫做test然後建立兩個Tabel,
在MSDN的範例裡有提供程式碼產生table
1: CREATE TABLE Users
2: (
3: UserName NVARCHAR(200) NOT NULL,
4: Pass NVARCHAR(200) NOT NULL
5: )
6:
7: CREATE TABLE UsersAudit
8: (
9: UserName NVARCHAR(200) NOT NULL
10: )
另外,SQL預設是關閉支援CLR的,所以我們要將它啟動
在SQL輸入
1: sp_configure 'clr enabled', 1
2: go
3: reconfigure
4: go
用來開啟支援CLR。
SQL端也準備好之後就可以測試了,我們可以在VS專案裡面找到Test.sql檔案,
可以在裡面寫下要測試的內容,然後按下F5,VS就會幫我們部屬到SQL Server上接著就Debug了。
另外我們也可以在SQL端查看部屬的情形
可以在Table的Trigger下看到此Trigger的名稱(非function的名稱,是SqlTrigger裡面的Name屬性)
然後在DB的Programmability底下看到此組件
根據MSDNhttp://msdn.microsoft.com/en-us/library/ms345101.aspx [注2]
有三種權限SAFE、EXTERNAL_ACCESS、UNSAFE,上面的網頁有說明可以做到的事情,
簡單來說SAFE只能連結DB內的資料。EXTERNAL_ACCESS可以連結外部資料,例如開檔案、Socket等。
而UNSAFE可以呼叫native code,也就是win32 API。
用此篇文章的方式產生的trigger權限是SAFE,能做的事情有限,如果要開檔案或是連線網路的話就不能用此權限。
下一篇會說明如何使用EXTERNAL_ACCESS。
參考資料:
How to: Create and Run a SQL Server Trigger by using Common Language Run-time Integration