无法执行CLR SQL函数

时间:2017-01-06 16:22:23

标签: 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.GetLocation(addr.Value);
        return new SqlString(string.Format("{0:N6}, {1:N6}", loc.LatLon.Latitude, loc.LatLon.Longitude));
    }
}

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

我似乎无法称呼它。我试过了:

select GeoCodeSqlLib.GeoCodeLib.GetLatLon('12143 Thompson Dr. Fayetteville, AR 72704')

这给了我一条消息,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

这给了我一条消息,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>

我得到的错误是:

System.Configuration.SettingsPropertyNotFoundException: The settings property 'GeoCodeSqlLib_ourserver_GeoCode' was not found.

1 个答案:

答案 0 :(得分:4)

一些事情:

  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;
    

    所以最终应该看起来像:

    CREATE FUNCTION dbo.GetLatLon(@Address NVARCHAR(4000))
    RETURNS NVARCHAR(4000)
    AS EXTERNAL NAME GeoCodeSqlLib.[{namespace_name}.GeoCodeLib].GetLatLon;
    
  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。

    < / LI>

    根据问题中的更新部分:

    不,您不需要使Visual Studio项目成为“数据库项目”,但它确实使发布更容易。

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

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

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

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

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

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

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

    6. 现在,您可以使用WITH PERMISSION_SET = EXTERNAL_ACCESS(在CREATE ASSEMBLYALTER ASSEMBLY语句中使用该特定密钥签名的任何程序集),而不会出现错误。

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

      Stairway to SQLCLR

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

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

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

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

        Does SQL Server CLR Integration support configuration files?