执行 CLR SQL 函数时出现问题

Pet*_*ete 3 c# sql-server sqlclr

我创建了一个简单的 1 方法 DLL,使用内部 Web 服务将字符串地址转换为纬度/经度字符串。代码很简单:

public class GeoCodeLib
{
    [SqlFunction(IsDeterministic=false)]
    public SqlString GetLatLon(SqlString addr)
    {
        Geo.GeoCode gc = new Geo.GeoCode();
        Geo.Location loc = gc.GetLocation(addr.Value);
        return new SqlString(string.Format("{0:N6}, {1:N6}", loc.LatLon.Latitude, loc.LatLon.Longitude));
    }
}
Run Code Online (Sandbox Code Playgroud)

我的目标是.NET Framework 2.0。我们使用的是 SQL Server 2008 R2。该程序集已添加到数据库中(该程序集称为 GeoCodeSqlLib.dll)。我们没有对其设置任何权限。

我似乎无法调用它。我试过了:

select GeoCodeSqlLib.GeoCodeLib.GetLatLon('12143 Thompson Dr. Fayetteville, AR 72704')
Run Code Online (Sandbox Code Playgroud)

这给了我这样的信息,Cannot find either column "GeoCodeSqlLib" or the user-defined function or aggregate "GeoCodeSqlLib.GeoCodeLib.GetLatLon", or the name is ambiguous.

我试过了:

CREATE FUNCTION GetLatLon(@amount varchar(max)) RETURNS varchar(max) 
AS EXTERNAL NAME GeoCodeSqlLib.GeoCodeLib.GetLatLon
Run Code Online (Sandbox Code Playgroud)

这给了我这样的信息,Could not find Type 'GeoCodeLib' in assembly 'GeoCodeSqlLib'.

我显然错过了一些东西。该文档的示例都是非常基本的,无论我缺少什么步骤,我只是无法在文档中找到它。

更新

感谢您的所有帮助。所以有几件事:

  • 我需要使其成为外部访问权限才能进行 Web 服务调用。我没有意识到我需要创建一个 SQL Server 项目。我只是在做一个常规的 C# 类库。
  • 所以我创建了 SQL Server 项目。SQL Server 项目不支持 Web 服务引用。我尝试添加对原始 DLL 的引用,因此新的 SQL Server 项目 DLL 充当原始 DLL(我原始问题中的代码)的代理。
  • 然而,这给了我:Assembly 'geocodesqllib, version ...' was not found in the SQL catalog.但我无法将其添加到目录中,因为它需要外部访问,而我无法从非 SQL Server 项目(据我所知)执行此操作。

他们真的不想让这件事变得容易,是吗?

更新2

根据我下面的评论,最后的问题似乎是配置文件。它没有找到设置。为了保护无辜者,服务器名称已更改:

<applicationSettings>
    <GeoCodeSqlLib.Properties.Settings>
        <setting name="GeoCodeSqlLib_ourserver_GeoCode" serializeAs="String">
            <value>http://ourserver/GeoCode.svc</value>
        </setting>
    </GeoCodeSqlLib.Properties.Settings>
</applicationSettings>
Run Code Online (Sandbox Code Playgroud)

我得到的错误是:

System.Configuration.SettingsPropertyNotFoundException: The settings property 'GeoCodeSqlLib_ourserver_GeoCode' was not found.
Run Code Online (Sandbox Code Playgroud)

Sol*_*zky 5

一些东西:

  1. 正如 Jeroen Mostert 在对该问题的评论中提到的,SQLCLR 方法必须声明为static.

  2. 在您的CREATE FUNCTION声明中,您需要使用NVARCHAR而不是VARCHAR. SQLCLR 不支持VARCHAR.

  3. 在您的CREATE FUNCTION语句中,无需使用MAX输入参数和返回类型。纬度和经度组合将始终少于 4000 个字符,并且我非常确定地址也将少于 4000 个字符。MAX即使签名中只有一种数据类型,也会对性能产生一定的影响。你最好让两者都成为NVARCHAR(4000)

  4. 如果您仍然收到“找不到类型”错误,则很可能您还有一个未在问题代码中显示的命名空间。语法EXTERNAL NAME是:

    EXTERNAL NAME AssemblyName.[NamespaceName.ClassName].MethodName;
    
    Run Code Online (Sandbox Code Playgroud)

    所以最终应该看起来像这样:

    CREATE FUNCTION dbo.GetLatLon(@Address NVARCHAR(4000))
    RETURNS NVARCHAR(4000)
    AS EXTERNAL NAME GeoCodeSqlLib.[{namespace_name}.GeoCodeLib].GetLatLon;
    
    Run Code Online (Sandbox Code Playgroud)
  5. 关于本声明:

    我的目标是.NET Framework 2.0。我们使用的是 SQL Server 2008 R2

    您最多可以使用 .NET Framework 版本 3.5,因为 3.0 和 3.5 在 CLR 2.0 中运行,并且 SQL Server 2005、2008 和 2008 R2 链接到 CLR 2.0。


根据更新问题中的

不,你不需要Visual Studio 项目设为“数据库项目”,但它确实使发布变得更容易一些。

为了将GeoCodeSqlLib库设置为EXTERNAL_ACCESS,您可以通过 Visual Studio 中数据库项目的项目属性来完成此操作。您还可以添加WITH PERMISSION_SET = EXTERNAL_ACCESS到您的CREATE ASSEMBLY语句中,这是 Visual Studio 发布过程(由 SSDT 处理)正在执行的所有操作。

现在,为了能够将任何 Assembly 设置为EXTERNAL_ACCESS(甚至设置为UNSAFE),您需要执行以下操作:

  1. 对程序集进行签名(也可以在 Visual Studio 项目属性中轻松完成)并确保“使用密码保护密钥文件”。

  2. 构建项目(而不是发布)以便它创建 DLL。

  3. master数据库中,从该 DLL 创建非对称密钥。

  4. 仍然在 中master,从该非对称密钥创建登录。

  5. 授予新的基于密钥的登录权限EXTERNAL ACCESS ASSEMBLY

现在您可以使用WITH PERMISSION_SET = EXTERNAL_ACCESS(CREATE ASSEMBLYALTER ASSEMBLY语句中)而不会出现错误。

首先更清楚地了解如何使用 SQLCLR 可能会有所帮助。请参阅我在 SQL Server Central 上撰写的有关此主题的系列文章(需要免费注册才能阅读该网站上的内容):

通往 SQLCLR 的阶梯

该系列中的第 7 级甚至描述了一种使用 Visual Studio / SSDT 来管理上述步骤以使其以自动化方式工作的方法,因为 VS / SSDT 不处理这方面的安全性(这很遗憾,因为它鼓励人们采取简单的方法将数据库设置为TRUSTWORTHY ON安全漏洞)。我的下一篇文章将介绍一种在 Visual Studio 中使用 T-4 模板的更简单的方法。


对于更新 2部分以及对此答案的其他评论:

  1. 由于每个数据库/所有者组合都有一个应用程序域,因此在特定程序集中执行代码的所有会话都将共享该确切的代码和内存。如果您有一个静态类变量(即在应用程序域的生命周期内保留其值的变量),那么它会在所有会话之间共享,这可能会导致意外/不可靠的行为。因此,使用静态类变量需要将程序集标记为UNSAFE. 否则,如果变量标记为 ,则可以在程序集中拥有静态类SAFE变量。EXTERNAL_ACCESSreadonly

  2. 关于应用程序配置文件,您可以使用它们,但本例中的“应用程序”是 SQL Server。因此,您始终可以将配置详细信息放在machine.config中配置对于特定 CLR 版本(在您的情况下为 CLR 2.0)的所有进程都是全局的,或者您可以为 SQL Server 创建一个配置文件,如我的这个答案所示,也在这里:

    SQL Server CLR 集成是否支持配置文件?