使用 Reflection 和 IDataReader.GetSchemaTable 创建读取和处理 IDataRead

本文关键字:读取 处理 创建 IDataRead GetSchemaTable Reflection IDataReader 使用 | 更新日期: 2023-09-27 18:01:30

我正在编写一个类,该类封装了使用 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();
}

关于该方法的正确性,不幸的是我现在无法测试,我有以下问题:

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

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

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

  1. DataRowCollection的底层数据结构是什么?即使这个问题不能回答,至少,使用DataRowCollection.Remove()DataRowCollection中删除DataRow的渐近计算复杂度是多少?

而且,关于该方法明显的丑陋,我有以下问题:

  1. 有没有办法从IDataReader中检索特定的元数据(例如,只是列的序号、名称和类型(,而不是完整的模式表?

  2. (string)field["ColumnName"] == name有必要string演员吗?.NET 如何比较恰好包含对string引用的object变量与string变量:按引用值还是按内部数据值?(当有疑问时,我宁愿在正确性方面犯错,因此演员表;但是,当能够消除所有怀疑时,我更喜欢这样做。

  3. 即使我使用 KeyValuePair<int, PropertyInfo> s 来表示匹配字段和属性对,这些对也不是实际的键值对。它们只是普通的普通 2 元组。但是,.NET Framework 的 2.0 版不提供元组数据类型,而且,如果我要创建自己的特殊用途元组,我仍然不知道在哪里声明它。在C++,最自然的地方是在方法内部。但这是 C#,方法内类型定义是非法的。我该怎么办?应对使用根据定义不是最合适的类型(KeyValuePair<int, PropertyInfo>(的不优雅,或者应对无法在最适合的地方声明类型?

使用 Reflection 和 IDataReader.GetSchemaTable 创建读取和处理 IDataRead

就 A1 而言,我相信在调用 IDataReader.NextResult() 之前,GetSchemaTable只会返回当前结果集的信息。

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

呵。

我可以回答其中的几个:

答2(是的,从GetSchemaTable中得出的列序号与用于索引器的列序号相同。

B1(我不确定,但这没关系,因为如果您在foreach中枚举时从DataRowCollection中删除它,您将抛出. 如果我是你,我会制作一个字段或属性的哈希表来帮助匹配它们,而不是担心这种线性搜索和删除。

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

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

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