Sql-Server

可以將一個 CLR 函式添加到 SQL 伺服器,但不能在同一程序集中添加第二個:載入錯誤的 DLL

  • April 6, 2017

使用以下程式碼,我使用 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因為那些是不允許的,但我通過創建一個幾乎相同簽名的空方法來測試這種情況,只是更改SqlStringSqlChars. 它也編譯和部署得很好,但這次沒有創建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 頁面的正則表達式選項

引用自:https://dba.stackexchange.com/questions/168573