SQL Server中的CLR編程——用.NET爲SQL Server編寫存儲過程和函數

很早就知道可以用.NET爲SQL Server2005及以上版本編寫存儲過程、觸發器和存儲過程的,不過之前開發的系統要麼因爲歷史原因用的是SQL2000要麼根本用不着在SQL Server中啓用CLR,所以一直沒有嘗試。最近因爲項目的原因,在這方面做了一個調研,現在在這裏分享一下心得。
首先要說明的是要在SQL Server中啓用CLR必須是在SQL Server2005及以上版本,其次在默認情況下是沒有啓用CLR的,必須要顯示設置爲啓用。比如我們要在ArticleCollectorDB數據庫中運行用.NET編寫的函數或者存儲過程,至少先要進行下面的SQL語句:

  1. exec sp_configure 'clr enabled', 1;--在SQL Server中啓用CLR 
  2. reconfigure; 
  3. go 
  4. --在ArticleCollectorDB數據庫中設置TRUSTWORTHY爲ON 
  5. ALTER DATABASE [ArticleCollectorDB] SET TRUSTWORTHY ON 


這時可能會得到提示要重新啓動SQL Server,如果有此提示則重新啓動一下。
接着我們在VS中進行編碼,在這裏我們將分別編寫一個名爲IsMatch的函數和一個名爲SendMail存儲過程。在VS中創建一個名爲NetSkycn.Data的類庫項目,添加一個SqlCLR的類,代碼如下:

  1. using System.Data.SqlTypes; 
  2. using System.Net; 
  3. using System.Net.Mail; 
  4. using System.Security.Permissions; 
  5. using System.Text.RegularExpressions; 
  6. using Microsoft.SqlServer.Server; 
  7.  
  8. namespace NetSkycn.Data 
  9. /// <summary> 
  10. /// 在SQL Server環境中執行的CLR方法,注意提供給SQL Server調用的方法必須有SqlFunction/SqlProcedure Attribute 
  11. /// 作者:周公 
  12. /// 創建日期:2012-05-09 
  13. /// 博客地址:http://blog.csdn.net/zhoufoxcn http://zhoufoxcn.blog.51cto.com 
  14. /// 新浪微博地址:http://weibo.com/zhoufoxcn 
  15. /// </summary> 
  16. public sealed class SqlCLR 
  17. /// <summary> 
  18. /// 判斷字符串是否匹配正則表達式 
  19. /// </summary> 
  20. /// <param name="source">要匹配的文本</param> 
  21. /// <param name="pattern">進行匹配的正則表達式</param> 
  22. /// <param name="options">正則表達式匹配選項,1爲忽略大小寫,2爲多行匹配,3爲忽略大小寫且多行匹配</param> 
  23. /// <returns></returns> 
  24. [SqlFunction(IsDeterministic = true, DataAccess = DataAccessKind.None)] 
  25. public static SqlBoolean IsMatch(string source, string pattern,int options) 
  26. if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(pattern)) 
  27. return SqlBoolean.False; 
  28. RegexOptions regexOptions=RegexOptions.None; 
  29. int optionIgnoreCase = 1; 
  30. int optionMultiline = 2; 
  31. if ((options & optionIgnoreCase) != 0) 
  32. regexOptions = regexOptions | RegexOptions.IgnoreCase; 
  33.  
  34. if ((options & optionMultiline) != 0) 
  35. regexOptions = regexOptions | RegexOptions.Multiline; 
  36.  
  37. return (SqlBoolean)(Regex.IsMatch(source, pattern, regexOptions)); 
  38. /// <summary> 
  39. /// 發送郵件 
  40. /// </summary> 
  41. /// <param name="to">收件人郵件地址</param> 
  42. /// <param name="from">發件人郵件地址</param> 
  43. /// <param name="subject">郵件主題</param> 
  44. /// <param name="body">郵件內容</param> 
  45. /// <param name="username">登錄smtp主機時用到的用戶名,注意是郵件地址'@'以前的部分</param> 
  46. /// <param name="password">登錄smtp主機時用到的用戶密碼</param> 
  47. /// <param name="smtpHost">發送郵件用到的smtp主機</param> 
  48. [SqlProcedure] 
  49. [SmtpPermission(SecurityAction.Assert)] 
  50. [SecurityPermission(SecurityAction.Assert)] 
  51. public static void SendMail(string to, string from, string subject, string body, string userName, string password, string smtpHost) 
  52. MailAddress addressFrom = new MailAddress(from); 
  53. MailAddress addressTo = new MailAddress(to); 
  54. MailMessage message = new MailMessage(addressFrom, addressTo); 
  55. message.Subject = subject;//設置郵件主題 
  56. message.IsBodyHtml = true;//設置郵件正文爲html格式 
  57. message.Body = body;//設置郵件內容 
  58.  
  59. SmtpClient client = new SmtpClient(smtpHost); 
  60.  
  61. //設置發送郵件身份驗證方式 
  62. //注意如果發件人地址是[email protected],則用戶名是abc而不是[email protected] 
  63. client.Credentials = new NetworkCredential(userName, password); 
  64. client.Send(message); 
  65.  


編譯通過之後,記住類庫的物理全路徑,比如:F:\VS2008\netskycn\NetSkycn.Data\bin\Release\NetSkycn.Data.dll,在這裏要強調幾點:一、對於將來提供給SQL Server調用的函數或者存儲過程必須是靜態方法,並且還必須帶有SqlFunction或者SqlProcedure屬性;二、對於一些需要訪問外部網絡資源和安全屬性的還必須添加響應的屬性(如本例中的SendMail方法,如果沒有添加響應的屬性在創建SQL Function/Procedure時會出現錯誤提示)。
現在我們開始遵循先爲SQL Server創建程序集、後創建函數或者存儲過程的順序來操作,在操作過程中用到的SQL語句如下:

  1. --在ArticleCollectorDB數據庫中設置TRUSTWORTHY爲ON 
  2. ALTER DATABASE [ArticleCollectorDB] SET TRUSTWORTHY ON 
  3.  
  4. --如果已經存在該對象則刪除 
  5. IF EXISTS(SELECT * FROM SYS.SYSOBJECTS WHERE NAME='SendMail' AND XTYPE='PC')  
  6. DROP PROCEDURE SendMail 
  7.  
  8. --如果已經存在該對象則刪除 
  9. IF EXISTS(SELECT * FROM SYS.SYSOBJECTS WHERE NAME='IsMatch' AND XTYPE='FS')  
  10. DROP FUNCTION IsMatch 
  11.  
  12. --如果已經存在SqlCLR程序集則刪除該程序集 
  13. IF EXISTS(SELECT * FROM SYS.ASSEMBLIES WHERE NAME='SqlCLR')  
  14. DROP ASSEMBLY SqlCLR 
  15.  
  16. --在SQL Server中創建程序集,,創建的程序集名爲SqlCLR 
  17. CREATE ASSEMBLY SqlCLR FROM 'F:\VS2008\netskycn\NetSkycn.Data\bin\Release\NetSkycn.Data.dll' WITH PERMISSION_SET = UNSAFE 
  18. GO 
  19.  
  20. --從CLR程序集中創建函數,函數名爲IsMatch,有三個參數, 
  21. --[SqlCLR]是SQL Server中程序集名 
  22. --[NetSkycn.Data.SqlCLR]是.NET中的類的全名(命名空間及類名) 
  23. --[IsMatch]是.NET中類的函數名 
  24. CREATE FUNCTION [dbo].[IsMatch]  
  25. (  
  26. @source AS NVARCHAR(200), 
  27. @pattern AS NVARCHAR(200), 
  28. @option INT=3 
  29. )  
  30. RETURNS BIT  
  31. AS  
  32. EXTERNAL NAME [SqlCLR].[NetSkycn.Data.SqlCLR].[IsMatch];  
  33. GO 
  34.  
  35. --從CLR程序集中創建函數,函數名爲IsMatch,有三個參數, 
  36. --[SqlCLR]是SQL Server中程序集名 
  37. --[NetSkycn.Data.SqlCLR]是.NET中的類的全名(命名空間及類名) 
  38. --[SendMail]是.NET中類的函數名 
  39. CREATE PROCEDURE [dbo].[SendMail]  
  40. (  
  41. @to AS NVARCHAR(200), 
  42. @from AS NVARCHAR(200), 
  43. @subject AS NVARCHAR(200), 
  44. @body AS NVARCHAR(MAX), 
  45. @userName AS NVARCHAR(200), 
  46. @password AS NVARCHAR(200), 
  47. @smtpHost AS NVARCHAR(200) 
  48. AS  
  49. EXTERNAL NAME [SqlCLR].[NetSkycn.Data.SqlCLR].[SendMail];  
  50. GO 


如果沒有得到任何錯誤提示,則表示創建函數和存儲過程成功。至此我們會看到如下情形:


這表示創建成功。
測試創建函數的SQL語句(查找article表中title字段是3至5個字段的數據):

  1. select * from article where dbo.IsMatch(Title,'^[\u4e00-\u9fa5]{3,5}$',3)=1 


測試創建存儲過程的SQL語句:

  1. exec [dbo].SendMail @to='[email protected]',@from='[email protected]',@subject='test',@body='This mail was sent by SQL Procedure',@userName='webmaster',@password='123',@smtpHost='smtp.qq.com' 


以上代碼在SQL Server 2005中文企業版、SQL Server 2008英文企業版測試通過。
可以看出在一些SQL語句不夠靈活的情況下,可以使用.NET來編寫存儲過程和函數,通過以上步驟之後和調用SQL語句寫的存儲過程和函數沒有區別,極大地方便了編程。

周公
2012-05-12

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章