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()
。因此:
- 在第一个
using
(即 string _TempReturn = String.Empty;
)上方创建一个字符串
- 在最里面的
using
中,将 return
替换为:_TempReturn = streamReader.ReadToEnd();
- 在最后一个
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
构造函数中指定正确的编码。
我在 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()
。因此:
- 在第一个
using
(即string _TempReturn = String.Empty;
)上方创建一个字符串 - 在最里面的
using
中,将return
替换为:_TempReturn = streamReader.ReadToEnd();
- 在最后一个
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
构造函数中指定正确的编码。