c#-无法执行CLR SQL函数
作者:互联网
我创建了一个简单的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')
这给了我消息,找不到列“ GeoCodeSqlLib”或用户定义的函数或聚合“ GeoCodeSqlLib.GeoCodeLib.GetLatLon”,或者名称不明确.
我试过了:
CREATE FUNCTION GetLatLon(@amount varchar(max)) RETURNS varchar(max)
AS EXTERNAL NAME GeoCodeSqlLib.GeoCodeLib.GetLatLon
这给了我消息,在程序集“ GeoCodeSqlLib”中找不到类型“ GeoCodeLib”.
我显然缺少了一些东西.文档的样本都是非常基本的,无论我缺少什么步骤,我都无法在文档中找到它.
更新资料
感谢您的所有帮助.有几件事:
>我需要对其进行外部访问才能进行Web服务调用..我没有意识到我需要创建SQL Server项目.我只是在做一个普通的C#类库.
>因此,我创建了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>
我得到的错误是:
System.Configuration.SettingsPropertyNotFoundException: The settings property 'GeoCodeSqlLib_ourserver_GeoCode' was not found.
解决方法:
一些东西:
>正如Jeroen Mostert在对该问题的评论中提到的那样,必须将SQLCLR方法声明为静态方法.
>在CREATE FUNCTION语句中,您需要使用NVARCHAR而不是VARCHAR. SQLCLR不支持VARCHAR.
>在您的CREATE FUNCTION语句中,无需使用MAX作为输入参数和返回类型.经度和纬度的组合将始终少于4000个字符,而且我很确定地址也将少于4000个字符.签名中甚至只有一个MAX数据类型都会对性能产生一定的影响.您最好将它们都设置为NVARCHAR(4000).
>如果仍然出现“找不到类型”错误,则很可能还存在问题代码中未显示的名称空间. 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;
>关于此声明:
I have targeted .NET Framework 2.0. We’re using SQL Server 2008 R2
您最多可以使用.NET Framework 3.5版,因为3.0和3.5在CLR 2.0中运行,并且SQL Server 2005、2008和2008 R2链接到CLR 2.0.
根据问题中的UPDATE部分:
不,您不需要将Visual Studio Project设置为“数据库项目”,但是确实使发布变得容易一些.
为了将GeoCodeSqlLib库设置为EXTERNAL_ACCESS,可以通过Visual Studio中数据库项目的项目属性来实现.您还可以在您的CREATE ASSEMBLY语句中添加WITH PERMISSION_SET = EXTERNAL_ACCESS,这是Visual Studio发布过程(由SSDT处理)所做的所有工作.
现在,为了能够将任何程序集设置为EXTERNAL_ACCESS(甚至设置为UNSAFE),您需要执行以下操作:
>对程序集签名(也可以在Visual Studio项目属性中轻松完成),并确保“使用密码保护密钥文件”.
>构建项目(不发布),以便它创建DLL.
>在主数据库中,从该DLL创建一个非对称密钥.
>仍然处于主状态,从该非对称密钥创建一个登录名.
>授予新的基于密钥的登录EXTERNAL ACCESS ASSEMBLY权限.
现在,您可以使用WITH PERMISSION_SET = EXTERNAL_ACCESS(对于使用该特定键签名的任何程序集,在CREATE ASSEMBLY或ALTER ASSEMBLY语句中使用)都不会出错.
可能有助于首先更清楚地了解如何使用SQLCLR.请在SQL Server Central上查看我正在就此主题撰写的系列文章(需要免费注册才能阅读该站点上的内容):
该系列的第7级甚至描述了一种使用Visual Studio / SSDT来管理上述步骤以自动化方式进行操作的方法,因为VS / SSDT无法处理安全性的这一方面(这令人遗憾,因为它鼓励人们可以轻松地将数据库设置为TRUSTWORTHY ON(这是一个安全漏洞).我的下一篇文章将介绍在Visual Studio中使用T-4模板的一种更简单的方法.
对于“更新2”部分以及对此答案的其他评论:
>由于每个数据库/所有者组合只有一个应用程序域,因此在特定程序集中执行代码的所有会话将共享该确切的代码和内存.如果您有静态的类变量(即在App Domain的生命周期内保留其值的变量),则该变量将在所有会话之间共享,这可能导致意外/不可靠的行为.因此,使用静态类变量要求将该程序集标记为UNSAFE.否则,如果变量被标记为只读,则可以在SAFE和EXTERNAL_ACCESS程序集中具有静态类变量.
>关于应用程序配置文件,您可以使用它们,但是在这种情况下,“应用程序”是SQL Server.因此,您始终可以将配置详细信息放置在特定CLR版本(在您的情况下为CLR 2.0)的所有进程所用的machine.config中,或者可以为SQL Server创建配置文件,如我的以下答案所示:同样在这里:
Does SQL Server CLR Integration support configuration files?
标签:sqlclr,c,sql-server 来源: https://codeday.me/bug/20191026/1936017.html