.net 如何通过SqlDataReader获取结果集中字符串字段的排序规则?
从中检索结果集时,可以使用该方法获取该结果集的大部分元数据。然而,缺少的一点是字符串字段的排序(即.net 如何通过SqlDataReader获取结果集中字符串字段的排序规则?,.net,sql-server,collation,sqldatareader,sqlclr,.net,Sql Server,Collation,Sqldatareader,Sqlclr,从中检索结果集时,可以使用该方法获取该结果集的大部分元数据。然而,缺少的一点是字符串字段的排序(即CHAR,VARCHAR,NCHAR,NVARCHAR,SQL\u VARIANT——如果包含字符串类型——甚至是不推荐使用的文本和NTEXT)。有没有办法得到这些信息 虽然技术上可以调用具有LCID和SqlCompareOptions属性的方法,但这些属性返回与数据库默认排序规则相关的值,该排序规则可能是也可能不是任何特定字段的排序规则(尽管大多数人似乎只是假设它是相同的)或表达(由于)。这些属性
CHAR
,VARCHAR
,NCHAR
,NVARCHAR
,SQL\u VARIANT
——如果包含字符串类型——甚至是不推荐使用的文本和NTEXT
)。有没有办法得到这些信息
虽然技术上可以调用具有LCID
和SqlCompareOptions
属性的方法,但这些属性返回与数据库默认排序规则相关的值,该排序规则可能是也可能不是任何特定字段的排序规则(尽管大多数人似乎只是假设它是相同的)或表达(由于)。这些属性仅在源数据类型为SQL\u VARIANT
时提供准确信息,这并没有太大帮助
没有这些额外信息(至少相当于区域设置的“LCID”)的问题在于,尽管所有字符,无论源编码如何,都可以在.NET中表示,而不会丢失(因为.NET字符串是UTF-16小端),将结果集中的字段与其他字符串进行比较时,无法确定要使用的区域设置
每个字符串字段的排序规则信息肯定是SQL Server通过TDS流发送到客户端的数据流的一部分。它用于:
- 通过
SqlDataReader
类的方法公开LCID
。但是该方法被标记为internal
,因此我无法访问它。它似乎只在一个地方使用:
- 为SQLCLR存储过程和触发器中使用的
SqlPipe
方法提供结果集结构
- 根据情况,使用
SqlDataReader
的方法设置正确的编码。但是,尽管该方法是public
,但TextReader
类没有编码的属性;编码信息仅在内部使用
更新
澄清一下:我们希望有一种获取此信息的方法,而不需要程序集完全受信任/不安全
。预期用途是在控制台应用程序和SQLCLR对象(存储过程、函数等)中运行的代码。如果程序集(加载到SQL Server中时)需要具有外部访问的权限集
,则可以接受。但是,要求将SQLCLR程序集标记为不安全将不起作用
最终的最高理想是获得SQL Server中存在的完整排序规则名称(例如,Latin1\u General\u 100\u BIN2
),并将SQL Server中的程序集标记为SAFE
更新2
按照@Jonathan的说法,使用反射可以调用“internal”GetLocaleId
方法,并返回正确的LCID。但是,在SQLCLR对象中使用此代码时,如果程序集未标记为不安全
,则会出现以下异常:
Msg 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(对象对象对象、RuntimeMethodHandleInternal方法、RuntimeType父对象、UInt32调用标志)中
在System.RuntimeMethodHandle.PerformSecurityCheck(对象对象对象、IRuntimeMethodInfo方法、RuntimeType父对象、UInt32调用标志)中
在System.Reflection.RuntimeMethodInfo.Invoke(对象obj、BindingFlags invokeAttr、绑定器绑定器、对象[]参数、文化信息文化)
在System.Reflection.MethodBase.Invoke(对象obj,对象[]参数)处
看看这篇MSDN文章,它甚至提到(重点补充):
向沙盒域添加受限成员访问权限
…
例如,主机可能会向Internet应用程序授予Internet权限和RMA,以便Internet应用程序可以发出访问其自身程序集中私有数据的代码。由于访问仅限于具有相等或较小信任的程序集,因此Internet应用程序无法访问完全信任的程序集(如.NET Framework程序集)的成员
不幸的是,要求程序集不安全
违背了要求
老实说,这只是整个谜题的一部分。正如在第一个更新部分中所述,我们的目标是拥有这个和真正的排序规则名称,而这个名称目前似乎不存在于任何地方。因此,我向Microsoft发送了以下建议:
通过SqlDataReader公开SQL Server结果集的排序规则信息(由于visualstudio.uservoice.com被关闭,链接不再有效)
更新3
澄清:理想情况下,会有一个方法或属性返回用于给定字符串字段的精确排序规则(例如,Latin1\u General\u 100\u CI\u AS\u KS\u WS\u SC
)。但是,仅仅公开LCID
和SqlCompareOptions
可能不够好,因为这些属性不能传递以下信息:
- 排序规则版本(当前为80(即未指定)、90、100或140/甚至备用的0、1、2或3就足够了)
\u-BIN
vs\u-BIN2
CodePage
\u VSS
(变量选择器敏感;启动
var method = typeof (SqlDataReader).GetMethod("GetLocaleId", BindingFlags.NonPublic | BindingFlags.Instance);
using (var conn = new SqlConnection(My.Config.ConnectionStrings.SqlConnection))
{
using (var command = new SqlCommand("SELECT TOP 0 * FROM TestTable", conn))
{
conn.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
var schema = reader.GetSchemaTable();
// 1033 = Latin1_General_CI_AS (confirmed)
var collation2 = method.Invoke(reader, new object[] { 2 });
// 1048 = Romanian_CI_AS (Current : SQL_Romanian_CP1250_CI_AS, close enough!)
var collation3 = method.Invoke(reader, new object[] { 3 });
}
}
}
// using Z.Expressions;
var getLocalIdCompiled = Eval.Compile<Func<SqlDataReader, int, int>>("reader.GetLocaleId(value)", "reader", "value");
using (var conn = new SqlConnection(My.Config.ConnectionStrings.SqlConnection))
{
using (var command = new SqlCommand("SELECT TOP 0 * FROM TestTable", conn))
{
conn.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
var schema = reader.GetSchemaTable();
var collation2 = getLocalIdCompiled(reader, 2);
var collation3 = getLocalIdCompiled(reader, 3);
}
}
}