检查数据读取器中是否存在某列

49

有没有办法在不仅仅检查IndexOutOfRangeException的情况下查看IDataReader对象中是否存在某个字段?

本质上,我有一个方法,它接收一个基于IDataReader的对象并创建记录的强类型列表。在某一情况下,一个数据读取器具有其他数据读取器没有的一个字段。如果不必要,我不想重写提供给该方法的所有查询以包含这个字段的某种形式。到目前为止,我能够想到的唯一方法是将这1个独特的字段放入try/catch块中,如下所示。

try
{
    tmp.OptionalField = reader["optionalfield"].ToString();
}
catch (IndexOutOfRangeException ex)
{
    //do nothing
}

除了将“可选字段”添加到其他查询或复制加载方法以使用可选字段和不使用可选字段的两个版本之外,是否有更简洁的方式?

我也在2.0框架中。


1
我想知道为什么微软没有将此功能添加到DataReader中。 - FLICKER
7个回答

65

我最终找到了一个解决方案,使用reader.GetName(int)方法。我创建了下面的方法来包含这个逻辑。

public bool ColumnExists(IDataReader reader, string columnName)
{
    for (int i = 0; i < reader.FieldCount; i++)
    {
         if (reader.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
        {
            return true;
        }
    }

    return false;
}

9
你可以改进这个if语句:if(reader.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))。 - Brabbeldas
如果使用别名会怎样呢?那么比较肯定会出错吧? - Murphybro2

23
以下将为您提供一个数据读取器中列名字符串列表。(请记住,结果基于最后一次读取,因此可能与读取器读取的内容不同)。
var cols = reader.GetSchemaTable()
                 .Rows
                 .OfType<DataRow>()
                 .Select(row => row["ColumnName"]);

或者检查列是否存在:

public bool ColumnExists(IDataReader reader, string columnName)
{

  return reader.GetSchemaTable()
               .Rows
               .OfType<DataRow>()
               .Any(row => row["ColumnName"].ToString() == columnName);
}

这个使用了LINQ,需要.NET 3.5或更高版本。它不能在.NET 2.0上运行,而OP说他正在使用它。 - Scott
1
'ColumnExists'是一个不错的函数。但请注意,如上所述,示例代码存在问题。必须在比较中添加显式转换,否则它将使用引用比较并始终返回false。.Any(row =>(String)row["ColumnName"] == columnName); - Daniel James Bryars

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

1
这个使用了需要 .NET 3.5 或更高版本的 Linq。它不能在 .NET 2.0 上运行,而 OP 表示他正在使用该版本。 - Scott

8
这应该能行,试试这个:
private static bool ColumnExists(SqlDataReader reader, string columnName)
        {
            using (var schemaTable = reader.GetSchemaTable())
            {
                if (schemaTable != null)
                    schemaTable.DefaultView.RowFilter = String.Format("ColumnName= '{0}'", columnName);

                return schemaTable != null && (schemaTable.DefaultView.Count > 0);
            }
        }

6

看来我错了。我知道你的实际列名在里面,但我走了错误的路。这个参考帮助我有点明白了,但我仍然不确定是否有一种优雅的方法可以做到这一点。从上面的链接中改编而来,您可以使用以下方式获取所有列的列表:

List<string> myCols = new List<string>();
DataTable schema = reader.GetSchemaTable();
foreach (DataRow row in schema.Rows)
{
    myCols.Add(row[schema.Columns["ColumnName"]]);
}

很不幸,看起来你只能通过索引访问schema.Rows,所以我不确定你是否可以绕过先循环行再按名称检查的方法。在这种情况下,你原来的解决方案似乎更加优雅!
注意:我的原始答案建议通过以下方式检查列是否存在:reader.GetSchemaTable().Columns["optionalfield"]。

在深入研究GetSchemaTable()之后,它似乎获取完全不同的列元数据而不是底层表。像IsColumnSet和ColumnSize这样的列被呈现出来,而不是实际的字段。 - JamesEggers
你可以在这个页面的列表中找到我得到的列的列表: http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqldatareader.getschematable%28VS.80%29.aspx - JamesEggers

4

将它载入 DataTable,然后您可以检查列:

DataTable dataTable = new DataTable();
dataTable.Load(reader);
foreach (var item in dataTable.Rows) 
{
    bool columnExists = item.Table.Columns.Contains("ColumnName");
}

3

不需要太过复杂,只需这样:

bool bFieldExists = datareader.GetSchemaTable().Columns.Contains(strFieldName);

5
遗憾的是,在这种情况下,“Columns”指的是有关IDataReader的元数据列,而不是IDataReader列的列表。 GetSchemaTable() DataTable中的每一行都是datareader中的一列。 - JMD
1
如果你不局限于使用 .net 2.0,那么你可以这样做:bool bFieldExists = datareader.GetSchemaTable().Columns.Select(o => o.Name).Contains(myColumnName); - pqsk
它不起作用。 - Waqas Raja
datareader.GetSchemaTable().Columns.Select(o => o.Name).Contains(myColumnName); 这个也不起作用。 - Ganesh Kodiganti

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