【发布时间】:2016-05-13 11:09:44
【问题描述】:
从SqlDataReader 检索结果集时,可以使用GetSchemaTable 方法获取该结果集的大部分元数据。然而,缺少的一件事是字符串字段的排序规则(即CHAR、VARCHAR、NCHAR、NVARCHAR、SQL_VARIANT——如果包含字符串类型——甚至是已弃用的TEXT和NTEXT)。有什么办法可以得到这些信息吗?
虽然在技术上可以调用具有LCID 和SqlCompareOptions 属性的GetSqlString 方法,但这些属性返回与数据库 的默认排序规则相关的值,这可能或者可能不是任何特定字段的排序规则(即使大多数人似乎只是假设它是相同的)或表达式(由于Collation Precedence)。这些属性仅在源数据类型为 SQL_VARIANT 时提供准确的信息,这并不是很有帮助。
没有这个额外信息(至少是等同于区域设置的“LCID”)的问题在于,虽然所有字符,无论源编码如何,都可以在 .NET 中表示而不会丢失(因为 .NET 字符串是 UTF-16 Little Endian),在将结果集中的字段与其他字符串进行比较时,无法确定要使用的 Locale。
每个字符串字段的排序规则信息绝对是通过 TDS 流从 SQL Server 发送到客户端的result set meta-data 的一部分。使用:
- 通过
SqlDataReader类的GetLocaleId 方法公开LCID。但是该方法被标记为internal,所以我无权访问它。它似乎只用在一个地方:SqlBulkCopy。 - 为
SqlPipe的Send(SqlDataReader) 方法提供结果集结构,该方法用于SQLCLR 存储过程和触发器。 - 在
SqlDataReader的GetTextReader()方法中,根据情况设置合适的encoding。然而,虽然该方法是public,但TextReader类没有Encoding的属性;编码信息仅在内部使用。
更新
澄清一下:希望有一种方法来获取此信息,而无需完全信任程序集/UNSAFE。预期用途是在控制台应用程序和 SQLCLR 对象(存储过程、函数等)中运行的代码。如果程序集(加载到 SQL Server 时)需要有一个 PERMISSION_SET 或 EXTERNAL_ACCESS,这是可以接受的。但是要求将 SQLCLR 程序集标记为 UNSAFE 将不起作用。
最终的最高理想是获取 SQL Server 中存在的完整排序规则名称(例如 Latin1_General_100_BIN2),和在 SQL Server 中将程序集标记为 SAFE。
更新 2
使用反射,根据@Jonathan 的answer,可以调用“内部”GetLocaleId 方法,它确实返回正确的 LCID。但是,在 SQLCLR 对象中使用此代码时,如果 Assembly 未标记为 UNSAFE,则会出现以下异常:
消息 6522,第 16 级,状态 1,第 9 行
在执行用户定义的例程或聚合“GetFieldCollation”期间发生 .NET Framework 错误:System.MethodAccessException:尝试通过方法“UserDefinedFunctions.GetFieldCollation(System.Data.SqlTypes.SqlString, System.Data.SqlTypes.SqlBoolean)”访问方法“System.Data.SqlClient.SqlDataReader.GetLocaleId(Int32)”失败.
System.MethodAccessException:
在 System.RuntimeMethodHandle.PerformSecurityCheck(Object obj, RuntimeMethodHandleInternal 方法, RuntimeType parent, UInt32 invocationFlags)
在 System.RuntimeMethodHandle.PerformSecurityCheck(Object obj, IRuntimeMethodInfo 方法, RuntimeType parent, UInt32 invocationFlags)
在 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] 参数, CultureInfo 文化)
在 System.Reflection.MethodBase.Invoke(Object obj, Object[] 参数)
查看这篇 MSDN 文章 Walkthrough: Emitting Code in Partial Trust Scenarios,它甚至提到(强调添加):
向沙盒域添加 RestrictedMemberAccess
...
例如,主机可能授予 Internet 应用程序 Internet 权限和 RMA,以便 Internet 应用程序可以发出访问其自己程序集中的私有数据的代码。 由于访问仅限于同等或较低信任的程序集,因此 Internet 应用程序无法访问完全受信任的程序集的成员,例如 .NET Framework 程序集。
很遗憾,要求大会为UNSAFE 违反了要求。
而且,老实说,这实际上只是整个难题的一部分。如第一个 UPDATE 部分所述,目标是让这个 和 成为真正的排序规则名称,而目前任何地方似乎都不存在。因此,我向微软发送了以下建议:
通过 SqlDataReader 公开 SQL Server 结果集的排序信息(由于 visualstudio.uservoice.com 被关闭,链接不再有效)
更新 3
澄清一下:理想情况下会有一个方法或属性返回用于给定字符串字段的精确排序规则(例如Latin1_General_100_CI_AS_KS_WS_SC)。但是,仅仅公开LCID 和SqlCompareOptions 可能还不够好,因为这些属性没有传达以下信息:
- 排序规则版本(目前 80(即未指定)、90、100 或 140 / 甚至替代 0、1、2 或 3 就足够了)
-
_BIN与_BIN2 CodePage-
_VSS(变量选择器敏感;从 SQL Server 2017 开始,通过日语版 140 排序规则) -
_SC(Supplementary Character Aware - 即允许内置函数将NVARCHAR数据处理为 UTF-16 而不是 UCS-2) - SQL Server 排序规则与 Windows 排序规则
- SortID(仅用于 SQL Server 排序规则)
-
_UTF8(VARCHAR数据的 UTF-8 编码;SQL Server 2019 中的新增功能)
至少需要公开在表格数据流 (TDS) 中发送的附加信息。该信息是:
- 排序规则版本(目前 80(即未指定)、90、100 或 140 / 甚至替代 0、1、2 或 3 就足够了)
-
_BIN与_BIN2 - SortID(仅用于 SQL Server 排序规则)
-
_UTF8(VARCHAR数据的 UTF-8 编码;SQL Server 2019 中的新增功能)
SortID,因为它只用于 SQL Server 排序规则,所以我们得到:
- SQL Server 排序规则与 Windows 排序规则
SortID 或 LCID(取决于排序规则的类型),可用于派生:
CodePage
这仍然使以下选项目前下落不明:
-
_VSS(变量选择器敏感;从 SQL Server 2017 开始,通过日语版 140 排序规则) -
_SC(Supplementary Character Aware - 即允许内置函数将NVARCHAR数据处理为 UTF-16 而不是 UCS-2)
所以,也许目前这在技术上是不可能的,但我想我会问一下,以防万一我遗漏了什么。对我来说,这个信息尚不可用,这似乎很奇怪。
【问题讨论】:
标签: .net sql-server collation sqldatareader sqlclr