在迭代之前检测IDataReader是否包含特定字段

8

我正在使用IDataReader来填充一些业务对象,但是在运行时我不知道读取器中确切的字段是什么。任何不在读取器中的字段都将在生成的对象上保留为空值。如何测试读取器是否包含特定字段而不仅仅是用try/catch语句封装它?

6个回答

9
这应该可以解决问题:
    Public Shared Function ReaderContainsColumn(ByVal reader As IDataReader, ByVal name As String) As Boolean
        For i As Integer = 0 To reader.FieldCount - 1
            If reader.GetName(i).Equals(name, StringComparison.CurrentCultureIgnoreCase) Then Return True
        Next
        Return False
    End Function

或者(在C#中)
public static bool ReaderContainsColumn(IDataReader reader, string name)
{
    for (int i = 0; i < reader.FieldCount; i++) {
        if (reader.GetName(i).Equals(name, StringComparison.CurrentCultureIgnoreCase)) return true; 
    }
    return false;
}

:o)


7

GetSchemaTable方法不会列出返回数据结构的列。 - JamesEggers
2
是的,它确实可以。从reader.GetSchemaTable().Rows中,每一行的第一项都是列名。例如,reader.GetSchemaTable().Rows[0][0]会给我第一个列名。 - Rob Levine
上次我尝试过这样做,它给了我类似于列的schema_info。不过因为已经有一段时间了,所以我得再试一次。 - JamesEggers
4
不确定为什么人们会投反对票——这是最简洁的解决方案。 - Jim Arnold
同意Jim的观点,这确实是最好的解决方案。 - BenW
1
不确定这是否是最好的。将reader.GetSchemaTable().Rows.Cast<DataRow>().Select(x => (string)x["ColumnName"]).Contains(colName, StringComparer.OrdinalIgnoreCase)Enumerable.Range(0, reader.FieldCount).Select(reader.GetName).Contains(colName, StringComparer.OrdinalIgnoreCase)进行对比。FieldCount方法不仅更简洁,而且速度快得多。 - nawfal

4
Enumerable.Range(0, reader.FieldCount).Any(i => reader.GetName(i) == "ColumnName")

0
我使用过的最佳解决方案是这样做:
DataTable dataTable = new DataTable();
dataTable.Load(reader);
foreach (var item in dataTable.Rows) 
{
    bool columnExists = item.Table.Columns.Contains("ColumnName");
}

尝试通过 reader["ColumnName"] 访问它并检查是否为 null 或 DBNull 会抛出异常。


-3
虽然我不同意这种方法(我认为在访问数据时,你应该事先知道其形状),但我理解有例外情况。
您可以使用读取器加载数据表,然后进行迭代。然后,您可以检查列是否存在。这样做的性能会更低,但您不需要尝试/捕获块(因此,对于您的需求来说可能更高效)。

-4

你不能仅仅测试reader["field"]是否为null或DBNull,因为如果列不在reader中,则会抛出IndexOutOfRangeException异常。

我在我的映射层中用于创建领域对象和使用映射层的存储过程可能具有不同的列名的代码如下;您可以修改它以避免如果未找到该列则抛出异常并返回default(t)或null。

我知道这不是最优雅或最优解决方案(如果可以避免,则确实应该避免),但是旧的存储过程或Sql查询可能需要一个解决方法。

    /// <summary>
    /// Grabs the value from a specific datareader for a list of column names.
    /// </summary>
    /// <typeparam name="T">Type of the value.</typeparam>
    /// <param name="reader">Reader to grab data off of.</param>
    /// <param name="columnNames">Column names that should be interrogated.</param>
    /// <returns>Value from the first correct column name or an exception if none of the columns exist.</returns>
    public static T GetColumnValue<T>(IDataReader reader, params string[] columnNames)
    {
        bool foundValue = false;
        T value = default(T);
        IndexOutOfRangeException lastException = null;

        foreach (string columnName in columnNames)
        {
            try
            {
                int ordinal = reader.GetOrdinal(columnName);
                value = (T)reader.GetValue(ordinal);
                foundValue = true;
            }
            catch (IndexOutOfRangeException ex)
            {
                lastException = ex;
            }
        }

        if (!foundValue)
        {
            string message = string.Format("Column(s) {0} could not be not found.",
                string.Join(", ", columnNames));

            throw new IndexOutOfRangeException(message, lastException);
        }

        return value;
    }

1
那会表现不佳。 - DalSoft

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接