C# 使用Reflection和IDataReader.GetSchemaTable创建读取和处理IDataReader';s当前结果集

C# 使用Reflection和IDataReader.GetSchemaTable创建读取和处理IDataReader';s当前结果集,c#,ado.net,C#,Ado.net,我正在编写一个类,它封装了使用ADO.NET从数据库检索数据的复杂性。其核心方法是 private void Read<T>(Action<T> action) where T : class, new() { var matches = new LinkedList<KeyValuePair<int, PropertyInfo>>(); // Read the current result set's metadata.

我正在编写一个类,它封装了使用ADO.NET从数据库检索数据的复杂性。其核心方法是

private void Read<T>(Action<T> action) where T : class, new() {
    var matches = new LinkedList<KeyValuePair<int, PropertyInfo>>();

    // Read the current result set's metadata.
    using (DataTable schema = this.reader.GetSchemaTable()) {
        DataRowCollection fields = schema.Rows;

        // Retrieve the target type's properties.
        // This is functionally equivalent to typeof(T).GetProperties(), but
        // previously retrieved PropertyInfo[]s are memoized for efficiency.
        var properties = ReflectionHelper.GetProperties(typeof(T));

        // Attempt to match the target type's columns...
        foreach (PropertyInfo property in properties) {
            string name = property.Name;
            Type   type = property.PropertyType;

            // ... with the current result set's fields...
            foreach (DataRow field in fields) {

                // ... according to their names and types.
                if ((string)field["ColumnName"] == name && field["DataType"] == type) {

                    // Store all successful matches in memory.
                    matches.AddLast(new KeyValuePair<int, PropertyInfo>((int)field["ColumnOrdinal"], property));
                    fields.Remove(field);
                    break;
                }
            }
        }
    }

    // For each row, create an instance of the target type and set its
    // properties to the row's values for their matched fields.
    while (this.reader.Read()) {
        T result = new T();
        foreach (var match in matches)
            match.Value.SetValue(result, this.reader[match.Key], null);
        action(result);
    }

    // Go to the next result set.
    this.reader.NextResult();
}
private void Read(Action-Action),其中T:class,new(){
var matches=newlinkedlist();
//读取当前结果集的元数据。
使用(DataTable schema=this.reader.GetSchemaTable()){
DataRowCollection字段=schema.Rows;
//检索目标类型的属性。
//这在功能上等同于typeof(T).GetProperties(),但是
//为提高效率,会将以前检索到的属性信息[]存储起来。
var properties=ReflectionHelper.GetProperties(typeof(T));
//尝试匹配目标类型的列。。。
foreach(属性中的PropertyInfo属性){
字符串名称=property.name;
类型类型=property.PropertyType;
//…使用当前结果集的字段。。。
foreach(字段中的数据行字段){
//…根据他们的名字和类型。
如果((字符串)字段[“ColumnName”]==名称和字段[“DataType”]==类型){
//将所有成功的匹配项存储在内存中。
matches.AddLast(新的KeyValuePair((int)字段[“ColumnOrdinal”],属性));
字段。删除(字段);
打破
}
}
}
}
//对于每一行,创建目标类型的实例并设置其
//属性设置为其匹配字段的行值。
而(this.reader.Read()){
T结果=新的T();
foreach(匹配中的变量匹配)
match.Value.SetValue(结果,this.reader[match.Key],null);
行动(结果);
}
//转到下一个结果集。
this.reader.NextResult();
}
关于方法的正确性,不幸的是我现在无法测试,我有以下问题:

  • 当使用单个
    IDataReader
    从两个或多个结果集检索数据时,
    IDataReader.GetSchemaTable
    是否返回所有结果集的元数据,或仅返回与当前结果集对应的元数据

  • IDataReader.GetSchemaTable检索到的列序号是否等于索引器
    IDataReader[int]
    使用的序号?如果没有,有没有办法将前者映射到后者

  • 关于该方法的效率,我有以下问题:

  • 什么是
    DataRowCollection
    的底层数据结构?即使这个问题无法回答,至少,使用
    DataRowCollection.Remove()
    DataRowCollection
    中删除
    DataRow
    的渐进计算复杂性是多少
  • 而且,关于这种方法明显的丑陋,我有以下问题:

  • 是否有任何方法可以从
    IDataReader
    检索特定元数据(例如,仅检索列的序号、名称和类型),而不是完整的架构表

  • 是否需要在
    (字符串)字段[“ColumnName”]==name
    中强制转换到
    字符串
    ?NET如何将碰巧包含对
    字符串
    引用的
    对象
    变量与
    字符串
    变量进行比较:按引用值还是按内部数据值?(当有疑问时,我宁愿在正确性方面犯错误,也就是在演员阵容方面;但是,当我能够消除所有疑问时,我宁愿这样做。)

  • 尽管我使用
    KeyValuePair
    s来表示匹配字段和属性对,但这些对不是实际的键值对。它们只是普通的2元组。然而,2.0版的.NET Framework并没有提供元组数据类型,而且,如果我要创建自己的专用元组,我仍然不知道在哪里声明它。在C++中,最自然的地方是方法里面。但这是C#,在方法中类型定义是非法的。我该怎么办?处理使用根据定义不是最合适的类型(
    KeyValuePair
    )的不雅问题,或者处理无法在最适合的位置声明类型的问题


  • 我可以回答以下几个问题:

    A2)是的,
    GetSchemaTable
    中的列序号与索引器中使用的列序号相同

    B1)我不确定,但这无关紧要,因为如果在
    foreach
    中枚举数据行集合时从
    数据行集合中删除它,则会抛出。如果我是你,我会制作一个字段或属性的哈希表来帮助匹配它们,而不是担心这种线性搜索和删除

    编辑:我错了,这是一个谎言——正如爱德华多在下面指出的,它不会抛出。但是,如果你认为你可能有一个拥有超过几十个属性的类型,那么它仍然有点慢

    C2)是的,这是必要的,否则它将通过引用进行比较


    C3)无论如何,我倾向于使用
    KeyValuePair

    至于A1,我相信在
    IDataReader.NextResult()
    被envoke之前,
    GetSchemaTable
    只会返回当前结果集的信息

    然后,当
    NextResult()
    被envoked时,您必须再次执行
    GetSchemaTable
    以获取有关当前结果集的信息


    HTH.

    关于B1),我认为从
    foreach
    中的集合中删除元素是有效的,只要程序不继续下一次迭代。至少,迭代器是如何在C++中工作的。哦,实际上我没有意识到这一点。你说得对。我确定了我的答案。