从SqlDataReader检索结果集时,可以使用GetSchemaTable方法获取该结果集的大部分元数据。然而,缺少的一件事是字符串字段的整理(即CHAR
,VARCHAR
,NCHAR
,NVARCHAR
,SQL_VARIANT
- 如果包含字符串类型 - 甚至是已弃用的TEXT
和NTEXT
)。有没有办法获得这些信息?
虽然在技术上可以调用具有LCID
和SqlCompareOptions
属性的GetSqlString方法,但这些属性会返回与数据库<的默认排序规则相关的值/ em>,可能是也可能不是任何特定字段的校对(即使大多数人似乎只是假设它是相同的)或表达式(由于Collation Precedence)。这些属性仅在源数据类型为SQL_VARIANT
时提供准确的信息,这不是非常有用。
没有这些额外信息(至少相当于Locale的“LCID”)的问题是,尽管所有字符,无论源编码如何,都可以在.NET中表示而不会丢失(因为.NET字符串)是UTF-16 Little Endian),在将结果集中的字段与其他字符串进行比较时,无法确定要使用的Locale。
每个字符串字段的排序规则信息肯定是通过TDS流从SQL Server发送到客户端的result set meta-data的一部分。它用于:
LCID
类的GetLocaleId方法公开SqlDataReader
。但该方法标记为internal
,因此我无法访问它。它似乎只在一个地方使用:SqlBulkCopy。SqlPipe
encoding
方法,该方法用于SQLCLR存储过程和触发器。SqlDataReader
的{{1}}方法中设置正确的public
。但是,虽然该方法为TextReader
,但Encoding
类没有UNSAFE
的属性;编码信息仅在内部使用。更新
澄清:希望有一种方法来获取这些信息,而不需要大会完全信任/ PERMISSION_SET
。预期用途是在Console App和SQLCLR对象(存储过程,函数等)中运行的代码。如果程序集(加载到SQL Server中时)需要EXTERNAL_ACCESS
UNSAFE
,那么这是可以接受的。但要求将SQLCLR程序集标记为Latin1_General_100_BIN2
将不起作用。
最终的最高理想是获取SQL Server中存在的完整排序规则名称(例如SAFE
),和,SQL Server中的程序集标记为{{ 1}}。
更新2
使用Reflection,按照@ Jonathan的Send(SqlDataReader),可以调用“内部”GetLocaleId
方法,它确实返回正确的LCID。但是,在SQLCLR对象中使用此代码时,如果程序集未标记为UNSAFE
,则会出现以下异常:
Ms 6522,Level 16,State 1,Line 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文章GetTextReader(),它甚至提到了(强调添加):
向沙盒域添加RestrictedMemberAccess
...
例如,主机可能会授予Internet应用程序Internet权限和RMA,以便Internet应用程序可以发出访问其自己的程序集中的私有数据的代码。 由于访问仅限于具有相同或较低信任的程序集,因此Internet应用程序无法访问完全受信任的程序集(如.NET Framework程序集)的成员。
不幸的是,要求大会UNSAFE
违反要求。
而且,说实话,这只是整体难题的一部分。如第一个UPDATE部分所述,目标是让和成为真正的排序规则名称,而且目前似乎并不存在。因此,我向Microsoft发送了以下建议:
答案 0 :(得分:1)
您可以使用反射或表达式树轻松访问内部属性(性能)
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 });
}
}
}
整理:https://msdn.microsoft.com/en-us/library/ms143508(v=sql.105).aspx
修改强>
您可以通过反射或表达树访问所有内部,私人或其他内容。
您还可以使用Eval Expression.NET使其更加轻松!
// 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);
}
}
}
免责声明:我是该项目的所有者Eval Expression.NET