可以向 SQL Server 添加一个 CLR 函数,但不能在同一程序集中添加第二个:加载错误的 DLL

rsj*_*ffe 3 sql-server c# sql-clr sql-server-2014

使用以下代码,我使用 csc 来编译 dll。然后我将程序集添加到 sql server 2014 使用

CREATE ASSEMBLY ReplaceMultiWord from 'd:\bcp\ReplaceMultiWord.dll' WITH PERMISSION_SET = SAFE;
Run Code Online (Sandbox Code Playgroud)

然后成功添加了第一个功能

CREATE FUNCTION dbo.ReplaceMultiWord
(@inputString AS NVARCHAR(MAX), @replacementSpec AS XML) RETURNS NVARCHAR(MAX)
AS
EXTERNAL NAME ReplaceMultiWord.UserDefinedFunctions.ReplaceMultiWord;
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试添加第二个功能时

CREATE FUNCTION dbo.CanReplaceMultiWord
(@inputString AS NVARCHAR(MAX), @replacementSpec AS XML) RETURNS BIT
AS
EXTERNAL NAME ReplaceMultiWord.UserDefinedFunctions.CanReplaceMultiWord;
Run Code Online (Sandbox Code Playgroud)

我收到错误

Could not find method 'CanReplaceMultiWord' for type 'UserDefinedFunctions' in assembly 'ReplaceMultiWord'
Run Code Online (Sandbox Code Playgroud)

下面是我的代码。为什么 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Sol*_*zky 6

您的代码没有任何问题,至少不是问题中发布的代码。我将它复制并粘贴到 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....,这不仅更便于移植,而且您不可能拥有错误版本的程序集(假设您正在使用最近的部署/创建脚本)。

边注:

RegexOptions.Compiled选项与基于对象的 RegEx 一起使用时要小心,因为一旦方法超出范围,编译的定义就会被丢弃。我可能读错了您的代码,但看起来 RegEx 实际上只执行一次,这对于使用RegexOptions.Compiled. 您要么想摆脱RegexOptions.Compiled或切换到静态方法(当然,除非我误读了代码;-)。有关更多详细信息,请参阅正则表达式选项的 MSDN 页面。

  • 感谢您的帮助。我发现我做错了什么,它涉及一个愚蠢的复制和粘贴错误。当我运行“CREATE ASSEMBLY ReplaceMultiWord from 'd:\bcp\ReplaceMultiWord.dll' WITH PERMISSION_SET = SAFE;”时,我没有使用我构建的最新 dll 的名称。`ReplaceMultiWord.dll` 中没有第二个例程。感谢您的帮助,我对野鹅追逐表示歉意。 (2认同)