SQL 服务器 UDF SQLCLR 调用将字符转换为问号

SQL Server UDF SQLCLR Call Converts Characters Into Question Marks

我在 Google 或 SO 上找不到任何符合我的问题的内容。

在 SQL 服务器中,我有一个标量函数(我们称它为 dbo.MySqlStringFunction)。 此函数的作用是调用一个用 C# 编写的实用程序,该实用程序调用 ASP.Net 视图和 returns HTML 作为 SqlString。

SQL服务器中的函数定义为:

RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS EXTERNAL NAME [Utils.UserDefinedFunctions].[MySqlStringFunction]

简化后的C#代码为:

var request = (HttpWebRequest)WebRequest.Create("www.mydomain.com");

using (var response = (HttpWebResponse)request.GetResponse())
using (var stream = response.GetResponseStream())
{
   using (var streamReader = new StreamReader(stream, Encoding.UTF8)
   {
      return new SqlString(streamReader.ReadToEnd());
   }
}

当我将 C# 代码放入控制台应用程序并 运行 它时,我得到了应有的一切。

当我直接在我的浏览器中访问 URL 时,它完全按照应有的方式显示。

但是,当我执行 SELECT MySqlStringFunction() 时,™、§、¤ 等字符分别显示为 2 或 3 个问号。

它似乎介于 return new SqlString(..) 和 sql 函数返回的值之间,表明某些事情变得不稳定。但我不知道它可能是什么。

问题似乎出在 return 的位置。当前代码(如问题所示)在 3 个 using 块的中间 return,其中之一是正在读取的 UTF-8 流。这可能会使事情变得混乱,因为 SQLCLR 是与主 SQL 服务器内存隔离的内存,通常您不能 return 通过流。最好先关闭打开的流,让using块调用Dispose()。因此:

  1. 在第一个 using(即 string _TempReturn = String.Empty;)上方创建一个字符串
  2. 在最里面的 using 中,将 return 替换为:_TempReturn = streamReader.ReadToEnd();
  3. 在最后一个 using 右括号下方,添加:return new SqlString(_TempReturn);

(旧答案,近期会移除)

问题在于网页和 SQL 服务器之间的编码差异。您正在为网页使用 Encoding.UTF8(考虑到 UTF-8 是互联网最常见的编码,这很可能是正确的),但是 SQL 服务器(连同 .NET 和 Windows 通常)是 UTF-16 Little Endian。这就是代码点 127 以上的每个字符得到 2 或 3 个 ? 的原因:UTF-8 是一种多字节编码,每个字符使用 1、2 或 3 个字节,而 UTF-16 始终是2 字节(补充字符是 4 字节,但这是由于是一对双字节值)。

您需要先将编码转换为 UTF-16 Little Endian,或者在传回流时。并且,UTF-16 Little Endian 是 .NET 中的 Unicode 编码,而 Big Endian Unicode 指的是 "UTF-16 Big Endian"。所以你想转换成Unicode编码。

或者,情况可能相反:网页不是 UTF-8,在这种情况下,您在 StreamReader 中声明错误。如果这是真的,那么您需要在 StreamReader 构造函数中指定正确的编码。