我创建了一个简单的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'.
更新
感谢所有帮助。所以有一些事情:
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.
答案 0 :(得分:4)
一些事情:
正如Jeroen Mostert在对该问题的评论中所提到的,SQLCLR方法必须声明为static
。
在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;
关于此声明:
我的目标是.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
),您需要执行以下操作:
签署程序集(也可以在Visual Studio项目属性中轻松完成),并确保“使用密码保护密钥文件”。
构建项目(不发布),以便创建DLL。
在master
数据库中,从该DLL创建一个非对称密钥。
仍然在master
,从该非对称密钥创建一个登录。
授予新的基于密钥的登录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,因此在特定程序集中执行代码的所有会话将共享该确切的代码和内存。如果您有一个静态类变量(即一个在App Domain的生命周期内保留其值的变量),那么它将在所有会话中共享,这可能会导致意外/不可靠的行为。因此,使用静态类变量要求将程序集标记为UNSAFE
。否则,如果变量标记为SAFE
,则可以在EXTERNAL_ACCESS
和readonly
程序集中拥有静态类变量。
关于应用配置文件,您可以使用它们,但在这种情况下,“app”是SQL Server。因此,您始终可以将配置详细信息放在 machine.config 中,该配置详细信息对于特定CLR版本的所有进程(在您的情况下为CLR 2.0)是全局的,或者您可以为SQL Server创建配置文件在我的这个答案中显示,也在SO:
Does SQL Server CLR Integration support configuration files?