Azure SQL 托管实例上的 CLR 存储过程在执行时出错:“主机存储中的程序集与 GAC 中的程序集具有不同的签名”

Jam*_*esP 5 sql-server sql-clr azure-sql-managed-instance

我有一个 CLR 存储过程,它在从 SQL Server 2012 - 2017 部署到本地 SQL Server 实例时可以正确执行。我可以成功部署到 Azure SQL 托管实例,但是当我执行该过程时,出现以下错误:

无法加载文件或程序集“System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”或其依赖项之一。主机存储中的程序集与 GAC 中的程序集具有不同的签名。(来自 HRESULT 的异常:0x80131050)。

我曾尝试使用项目引用但无济于事 -System.Net.Http.dll可以部署到托管实例的唯一版本是执行时出错的版本。

Sol*_*zky 7

使用 SQLCLR 的要求/细微差别之一是加载到 GAC 和 SQL Server 中的任何程序集必须是完全相同的版本(即下至补丁级别,而不仅仅是 Major.Minor.*)。因此,您的本地实例可能都使用 4.7.2(或其他),但如果托管实例使用 4.7、4.7.1、4.8 或其他任何版本,那么您将收到该错误。如果您的本地实例之一在运行与您从中获取System.Net.Http.DLL 的那个版本不同的 .NET Framework 版本的服务器上,您甚至会收到此错误。当然,如果您提到的所有这些不同的实例都在同一台物理服务器上运行,那么无论如何都只涉及一个操作系统,所以当然它们都可以正常工作;-)。

您需要找出托管实例上使用的特定版本,并在您的 Azure 安装脚本中使用它。您不需要在本地使用相同的版本,因为对具有相同签名的类似版本的引用应该可以工作。

现在,如何在托管实例上找到确切的框架版本?以下查询似乎是获取该信息的最佳方式:

SELECT olm.[name], olm.[file_version]
FROM   sys.dm_os_loaded_modules olm
WHERE  olm.[name] LIKE N'%mscoreei.dll%';
Run Code Online (Sandbox Code Playgroud)

O.P. replied back that the query above returned a version of 4.7:2623.0 on the
Managed Instance, and a version of 4.7:3190.0 on the local (on prem) system.
 
This is the source of the "Assembly in host store has a different signature than assembly in GAC" error

因此,为了使其工作,您需要找到该 DLL 的特定版本并将其打包以加载到托管实例中。

PS 此问题是使用不受支持的 .NET Framework 库的缺点之一。它们将在 GAC 中(通常,对吗?)并且如果 Windows 更新(本地,或它们对托管实例的主机系统执行的任何操作)更新版本,那么您的 SQLCLR 项目将停止工作。而且,如果新的不受支持的框架 DLL 被转换为混合模式程序集(托管和非托管代码),那么您将无法将新版本加载到 SQL Server 中,并且需要重新编码您的项目以不使用那个不受支持的框架程序集。

PPS 你用System.Net.Http做什么?如果是用于 Web 服务的东西,那么您应该只使用System.Net.HttpWebRequestHttpWebResponse类。您将不得不做一些额外的编码来构造和解析 Web 请求的 XML,但这些类存在于完全支持的库中。

O.P. replied back with: "Correct, web services stuff."

Later, O.P. replied back with: "I've managed to successful deploy and execute a basic SQLCLR
procedure on Managed Instance using just HttpWebRequest and HttpWebResponse,
I think it'll be best to go down that line until Microsoft extend the list of natively supported libraries.."

PPPS 你可能想看看SQL#(我写的一个 SQLCLR 库),因为有一个存储过程可以处理这个,基于HttpWebRequest. 它处理大多数(如果不是全部)内部管理的 HTTP 标头,允许发送自定义标头(包括用户 ID/密码,如有必要),发送 POST / GET 数据,甚至是大多数人通常遗漏的其他一些东西。只是一个想法,虽然为了完全披露,虽然有一个免费版本,但INET_GetWebPages仅在完整(付费)版本中。