C# 执行CLR SQL函数时出现问题

C# 执行CLR SQL函数时出现问题,c#,sql-server,sqlclr,C#,Sql Server,Sqlclr,我创建了一个简单的1方法DLL,使用内部web服务将字符串地址转换为Lat/Lon字符串。代码就是这样的: public class GeoCodeLib { [SqlFunction(IsDeterministic=false)] public SqlString GetLatLon(SqlString addr) { Geo.GeoCode gc = new Geo.GeoCode(); Geo.Location loc = gc.Ge

我创建了一个简单的1方法DLL,使用内部web服务将字符串地址转换为Lat/Lon字符串。代码就是这样的:

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));
    }
}
我的目标是.NETFramework2.0。我们正在使用SQL Server 2008 R2。该程序集已添加到数据库(该程序集称为GeoCodeSqlLib.dll)。我们没有对它设置权限

我似乎不能称之为。我试过:

select GeoCodeSqlLib.GeoCodeLib.GetLatLon('12143 Thompson Dr. Fayetteville, AR 72704')
CREATE FUNCTION GetLatLon(@amount varchar(max)) RETURNS varchar(max) 
AS EXTERNAL NAME GeoCodeSqlLib.GeoCodeLib.GetLatLon
这给了我一个信息,
找不到列“GeoCodeSqlLib”或用户定义函数或聚合“GeoCodeSqlLib.GeoCodeLib.GetLatLon”,或者名称不明确。

我试过:

select GeoCodeSqlLib.GeoCodeLib.GetLatLon('12143 Thompson Dr. Fayetteville, AR 72704')
CREATE FUNCTION GetLatLon(@amount varchar(max)) RETURNS varchar(max) 
AS EXTERNAL NAME GeoCodeSqlLib.GeoCodeLib.GetLatLon
这给了我一条消息,
在程序集“GeoCodeSqlLib”中找不到类型“GeoCodeLib”。

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

更新

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

  • 我需要使其成为外部访问,以便能够进行web服务调用。。我没有意识到我需要创建一个SQL Server项目。我只是在做一个普通的C#class库
  • 因此,我创建了SQL Server项目。SQL Server项目不支持web服务引用。我尝试添加对原始DLL的引用,因此新的SQL Server项目DLL充当原始DLL的代理(原始问题中的代码)
  • 然而,这给了我:
    程序集“geocodesqllib,version…”在
    SQL目录。
    但我无法将其添加到目录中,因为它需要 外部访问,我不能从非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>
有几件事:

  • 正如Jeroen Mostert在一篇关于这个问题的评论中提到的,SQLCLR方法必须声明为
    static

  • CREATE FUNCTION
    语句中,需要使用
    NVARCHAR
    而不是
    VARCHAR
    。SQLCLR不支持
    VARCHAR

  • CREATE FUNCTION
    语句中,输入参数和返回类型不需要使用
    MAX
    。纬度和经度的组合总是少于4000个字符,我很确定地址也会少于4000个字符。签名中甚至只有一个
    MAX
    数据类型,这无疑会对性能造成影响。你最好让这两个都成为
    NVARCHAR(4000)

  • 如果仍然出现“找不到类型”错误,那么很可能问题代码中没有显示名称空间。
    外部名称
    语法为:

    EXTERNAL NAME AssemblyName.[NamespaceName.ClassName].MethodName;
    
    因此,最终的结果应该是:

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

    我的目标是.NETFramework2.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


  • 根据问题中的更新部分:

    不,您不需要将VisualStudio项目设置为“数据库项目”,但它确实使发布变得更容易

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

    现在,为了能够将任何程序集设置为
    外部访问
    (甚至设置为
    不安全
    ),您需要执行以下操作:

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

  • 生成项目(而不是发布),以便创建DLL

  • master
    数据库中,从该DLL创建一个非对称密钥

  • 仍然在
    master
    中,使用该非对称密钥创建登录名

  • 授予新的基于密钥的登录
    外部访问程序集
    权限

  • 现在,您可以使用具有权限的
    \u SET=EXTERNAL\u ACCESS
    (在
    CREATE ASSEMBLY
    ALTER ASSEMBLY
    语句中,为使用该特定键签名的任何程序集执行)而不会出现错误

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

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


    有关更新2的部分和关于此答案的其他评论:

  • 由于每个数据库/所有者组合都有一个应用程序域,因此在特定程序集中执行代码的所有会话都将共享该代码和内存。如果您有一个静态类变量(即在应用程序域的生命周期内保留其值的变量)然后它在所有会话中共享,这可能导致意外/不可靠的行为