可以將一個 CLR 函式添加到 SQL 伺服器,但不能在同一程序集中添加第二個:載入錯誤的 DLL
使用以下程式碼,我使用 csc 來編譯 dll。然後我使用將程序集添加到 sql server 2014
CREATE ASSEMBLY ReplaceMultiWord from 'd:\bcp\ReplaceMultiWord.dll' WITH PERMISSION_SET = SAFE;
然後成功添加了第一個功能
CREATE FUNCTION dbo.ReplaceMultiWord (@inputString AS NVARCHAR(MAX), @replacementSpec AS XML) RETURNS NVARCHAR(MAX) AS EXTERNAL NAME ReplaceMultiWord.UserDefinedFunctions.ReplaceMultiWord;
但是,當我嘗試添加第二個函式時
CREATE FUNCTION dbo.CanReplaceMultiWord (@inputString AS NVARCHAR(MAX), @replacementSpec AS XML) RETURNS BIT AS EXTERNAL NAME ReplaceMultiWord.UserDefinedFunctions.CanReplaceMultiWord;
我收到了錯誤
Could not find method 'CanReplaceMultiWord' for type 'UserDefinedFunctions' in assembly 'ReplaceMultiWord'
下面是我的程式碼。為什麼sql server 看不到第二個函式?
using System; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Text.RegularExpressions; using System.Collections.Generic; using System.Xml; public partial class UserDefinedFunctions { //TODO: Concurrency? private static readonly Dictionary<string, ReplaceSpecification> cachedSpecs = new Dictionary<string, ReplaceSpecification>(); [SqlFunction(IsDeterministic = true, IsPrecise = true, DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None)] public static SqlString ReplaceMultiWord(SqlString inputString, SqlXml replacementSpec) { //TODO: Implement something to drop things from the cache and use a shorter key. string s = replacementSpec.Value; ReplaceSpecification rs; if (!cachedSpecs.TryGetValue(s, out rs)) { var doc = new XmlDocument(); doc.LoadXml(s); rs = new ReplaceSpecification(doc); cachedSpecs[s] = rs; } string result = rs.GetResult(inputString.ToString()); return new SqlString(result); } [SqlFunction(IsDeterministic = true, IsPrecise = true, DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None)] public static SqlBoolean CanReplaceMultiWord(SqlString inputString, SqlXml replacementSpec) { string s = replacementSpec.Value; ReplaceSpecification rs; if (!cachedSpecs.TryGetValue(s, out rs)) { var doc = new XmlDocument(); doc.LoadXml(s); rs = new ReplaceSpecification(doc); cachedSpecs[s] = rs; } return rs.IsMatch(inputString.ToString()); } internal class ReplaceSpecification { internal ReplaceSpecification(XmlDocument doc) { Replacements = new Dictionary<string, string>(); XmlElement root = doc.DocumentElement; XmlNodeList nodes = root.SelectNodes("x"); string pattern = null; foreach (XmlNode node in nodes) { if (pattern != null) pattern = pattern + "|"; string find = node.Attributes["find"].Value.ToLowerInvariant(); string replace = node.Attributes["replace"].Value; pattern = pattern + Regex.Escape(find); Replacements[find] = replace; } if (pattern != null) { pattern = "(?:" + pattern + ")"; Regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); } } private Regex Regex { get; set; } private Dictionary<string, string> Replacements { get; set; } internal string GetResult(string inputString) { if (Regex == null) return inputString; return Regex.Replace(inputString, (Match m) => { string s; if (Replacements.TryGetValue(m.Value.ToLowerInvariant(), out s)) { return s; } else { throw new Exception("Missing replacement definition for " + m.Value); } }); } internal bool IsMatch(string inputString) { if (Regex == null) return false; return Regex.IsMatch(inputString); } } }
**在 srutzky 的幫助下,我發現了(愚蠢的)錯誤。**始終檢查您是否已更新該
CREATE ASSEMBLY
行以從最新版本的 DLL 中提取。當我將第二個函式添加到 DLL 時,我使用了不同的名稱,以便在出錯時可以恢復,但忘記正確更新文件路徑。一旦我這樣做了,這個神秘的錯誤就消失了。
您的程式碼沒有任何問題,至少不是問題中發布的程式碼。我將它複製並粘貼到 Visual Studio 2015 中,它編譯和部署沒有錯誤。我想也許你可能有一個重載的方法,
CanReplaceMultiWord
因為那些是不允許的,但我通過創建一個幾乎相同簽名的空方法來測試這種情況,只是更改SqlString
為SqlChars
. 它也編譯和部署得很好,但這次沒有創建dbo.CanReplaceMultiWord
. 我嘗試通過您的語句手動創建,CREATE FUNCTION
但收到一個錯誤,提示存在“不止一種方法”CanReplaceMultiWord
並且無法重載;我沒有得到任何關於“找不到方法”的資訊。因此,我認為您可能會以某種方式在
CREATE FUNCTION
語句中的該方法名稱中有一個不可列印的字元(可能是控製字元?)。嘗試刪除該EXTERNAL NAME AS
行並重新輸入。雖然,我確實從“編輯”頁面複製並粘貼了您的程式碼,所以如果您複製並粘貼到問題中,那麼它通常也會被複製到那裡(除非他們沒有或者可能在進入系統的途中被清理, 不確定)。儘管如此,我仍然發現語句中的方法名稱很可能有問題CREATE FUNCTION
(不知何故,因為它適用於我的系統),因為如果您的程序集名稱錯誤,您會得到:在數據庫“your_db_name”的 SQL 目錄中找不到程序集“xxxxxxxxxx”。
並且正確的程序集名稱但錯誤的類名稱返回:
在程序集“xxxxxxxxxx”中找不到類型“yyyyyyyyyyy”。
此外,我強烈建議使用 Visual Studio 進行編譯。“社區版”是免費的,所以沒有(好的)理由不使用它。有幾個選項(實際上很多)可以很好地處理csc.exe。即使您不將它用於最終部署(我沒有),它也非常適合開發和測試。
PS 使用 VS 的一個好處是它通過將 DLL 字節轉換為十六進製字元串來進行部署,這樣它就可以做到
CREATE ASSEMBLY FROM 0x4D....
,這不僅更便攜,而且你不可能擁有錯誤版本的程序集(假設你正在使用最新的部署/創建腳本)。邊注:
在使用基於對象的 RegEx 選項時要小心,
RegexOptions.Compiled
因為一旦方法超出範圍,編譯的定義就會被丟棄。我可能讀錯了您的程式碼,但看起來正則表達式實際上只執行了一次,這對於使用RegexOptions.Compiled
. 您要麼想要擺脫RegexOptions.Compiled
或切換到靜態方法(當然,除非我誤讀了程式碼;-)。有關更多詳細資訊,請參閱 MSDN 頁面的正則表達式選項。