当前位置:  开发笔记 > 编程语言 > 正文

检查SqlDataReader对象中的列名称

如何解决《检查SqlDataReader对象中的列名称》经验,为你挑选了9个好方法。

如何检查SqlDataReader对象中是否存在列?在我的数据访问层中,我创建了一个方法,为多个存储过程调用构建相同的对象.其中一个存储过程具有另一个列,其他存储过程不使用该列.我想修改方法以适应每个场景.

我的应用程序是用C#编写的.



1> Chad Grant..:

Exception在某些其他答案中使用s作为控制逻辑被认为是不好的做法并且具有性能成本.

如果你经常使用它,那么在字段中循环会有很小的性能损失,你可能想要考虑缓存结果

更合适的方法是:

public static class DataRecordExtensions
{
    public static bool HasColumn(this IDataRecord dr, string columnName)
    {
        for (int i=0; i < dr.FieldCount; i++)
        {
            if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
                return true;
        }
        return false;
    }
}



2> Jasmine..:

使用这个布尔函数要好得多:

r.GetSchemaTable().Columns.Contains(field)

一个电话 - 没有例外.它可能会在内部抛出异常,但我不这么认为.

注意:在下面的评论中,我们想出了......正确的代码实际上是这样的:

public static bool HasColumn(DbDataReader Reader, string ColumnName) { 
    foreach (DataRow row in Reader.GetSchemaTable().Rows) { 
        if (row["ColumnName"].ToString() == ColumnName) 
            return true; 
    } //Still here? Column not found. 
    return false; 
}


@Jasmine:我说得太快了!您的代码检查架构表中的列,而不是结果集.您需要将"field"(假设"field"是列名)与每行的"ColumnName"字段的值进行比较.当你找到它时休息,如果你没有,则返回false.
@Steve J:结果集什么时候在GetSchemaTable中没有列?
是的,这不起作用.谁投了这么多次??? 如果这个答案不在这里,它会为我节省大量的调试时间!

3> Matt Hamilto..:

我认为你最好的选择是在你的DataReader上预先调用GetOrdinal("columnName"),并在列不存在时捕获IndexOutOfRangeException.

实际上,让我们做一个扩展方法:

public static bool HasColumn(this IDataRecord r, string columnName)
{
    try
    {
        return r.GetOrdinal(columnName) >= 0;
    }
    catch (IndexOutOfRangeException)
    {
        return false;
    }
}

编辑

好吧,这篇文章最近开始收集一些下注,我不能删除它,因为它是公认的答案,所以我要更新它,并且(我希望)试图证明使用异常处理是合理的.控制流.

Chad Grant发布的另一种实现方法是循环访问DataReader中的每个字段,并对您要查找的字段名称进行不区分大小写的比较.这将非常有效,并且真实地可能比我上面的方法表现得更好.当然,我绝不会在性能问题的循环中使用上面的方法.

我可以想到一种情况,其中try/GetOrdinal/catch方法将在循环不起作用的情况下工作.然而,这是一个完全假设的情况,所以这是一个非常脆弱的理由.无论如何,忍受我,看看你的想法.

想象一个允许您在表中"别名"列的数据库.想象一下,我可以使用名为"EmployeeName"的列定义一个表,但也给它一个别名"EmpName",并且对任一名称执行select会返回该列中的数据.和我一起到目前为止?

现在想象一下,该数据库有一个ADO.NET提供程序,并且它们为它编写了一个IDataReader实现,它将列别名考虑在内.

现在,dr.GetName(i)(在乍得的答案使用)只能返回一个字符串,所以它必须只返回一个列上的"别名"的.但是,GetOrdinal("EmpName")可以使用此提供程序字段的内部实现来检查每个列的别名,以查找您要查找的名称.

在这个假设的"别名列"情况下,try/GetOrdinal/catch方法将是确保您在结果集中检查列名称的每个变体的唯一方法.

劣质的?当然.但值得一想.老实说,我更倾向于IDataRecord上的"官方"HasColumn方法.


当我最初发布这个问题时,每个人都会忽略一件小事......我在2008年8月8日问了这个问题,Matt在2008年12月17日发布了他的回答.每个人都对捕获控制逻辑的异常感到不好,但直到2009年5月1日才提供可靠的替代解决方案.这就是为什么它最初被标记为答案.我今天仍在使用此解决方案.
只有当列不存在时,才会有性能损失.所描述的其他方法每次都会受到性能影响,并且会有更大的性能影响.虽然避免对控制流使用异常处理通常是不好的做法,但是如果不首先考虑它是否适用于您的情况,则不应排除此解决方案.
使用控制逻辑的异常?不不不
+1.我对"不要将控制逻辑的异常使用"作为一个广泛的设计规则感到满意.它并不意味着"不惜一切代价避免它".答案是一个记录很好的解决方法,正如@Nick所说,性能命中(如果有的话)只在列不存在时才会出现.
使用异常作为控制逻辑,根据我的经验,调试也变得更加麻烦。您必须取消选中“公共语言运行时异常”上的“引发”,然后当您收到真正的异常时,它可能在某个地方的处理程序中中断,而不是在出现问题的行中。

4> Larry..:

在一行中,在DataReader检索后使用它:

var fieldNames = Enumerable.Range(0, dr.FieldCount).Select(i => dr.GetName(i)).ToArray();

然后,

if (fieldNames.Contains("myField"))
{
    var myFieldValue = dr["myField"];
    ...

编辑

更高效的单行程,不需要加载模式:

var exists = Enumerable.Range(0, dr.FieldCount).Any(i => string.Equals(dr.GetName(i), fieldName, StringComparison.OrdinalIgnoreCase));



5> 小智..:

以下是Jasmin的想法的工作示例:

var cols = r.GetSchemaTable().Rows.Cast().Select
    (row => row["ColumnName"] as string).ToList(); 

if (cols.Contains("the column name"))
{

}



6> 小智..:

这对我有用:

bool hasColumnName = reader.GetSchemaTable().AsEnumerable().Any(c => c["ColumnName"] == "YOUR_COLUMN_NAME");



7> 小智..:

以下内容很简单,对我有用:

 bool hasMyColumn = (reader.GetSchemaTable().Select("ColumnName = 'MyColumnName'").Count() == 1);



8> Levitikon..:

如果您阅读了这个问题,Michael会询问DataReader,而不是DataRecord的人.让你的对象正确.

使用r.GetSchemaTable().Columns.Contains(field)on DataRecord确实有效,但它返回BS列(见下面的截图).

要查看数据列是否存在并包含DataReader中的数据,请使用以下扩展名:

public static class DataReaderExtensions
{
    /// 
    /// Checks if a column's value is DBNull
    /// 
    /// The data reader
    /// The column name
    /// A bool indicating if the column's value is DBNull
    public static bool IsDBNull(this IDataReader dataReader, string columnName)
    {
        return dataReader[columnName] == DBNull.Value;
    }

    /// 
    /// Checks if a column exists in a data reader
    /// 
    /// The data reader
    /// The column name
    /// A bool indicating the column exists
    public static bool ContainsColumn(this IDataReader dataReader, string columnName)
    {
        /// See: http://stackoverflow.com/questions/373230/check-for-column-name-in-a-sqldatareader-object/7248381#7248381
        try
        {
            return dataReader.GetOrdinal(columnName) >= 0;
        }
        catch (IndexOutOfRangeException)
        {
            return false;
        }
    }
}

用法:

    public static bool CanCreate(SqlDataReader dataReader)
    {
        return dataReader.ContainsColumn("RoleTemplateId") 
            && !dataReader.IsDBNull("RoleTemplateId");
    }

调用r.GetSchemaTable().ColumnsDataReader返回BS列:

在DataReader中调用GetSchemeTable


“正确摆放你的物品。” -但是`IDataReader`实现了`IDataRecord`。它们是同一对象的不同接口-就像`ICollection <T>`和`IEnumerable <T>`是List <T>`的不同接口一样。IDataReader允许前进到下一条记录,而IDataRecord允许从当前记录读取。此答案中使用的方法全部来自“ IDataRecord”接口。请参阅/sf/ask/17360801/,以获取有关为什么最好将参数声明为“ IDataRecord”的说明。

9> 小智..:

我为Visual Basic用户写过:

Protected Function HasColumnAndValue(ByRef reader As IDataReader, ByVal columnName As String) As Boolean
    For i As Integer = 0 To reader.FieldCount - 1
        If reader.GetName(i).Equals(columnName) Then
            Return Not IsDBNull(reader(columnName))
        End If
    Next

    Return False
End Function

我认为这更强大,用法是:

If HasColumnAndValue(reader, "ID_USER") Then
    Me.UserID = reader.GetDecimal(reader.GetOrdinal("ID_USER")).ToString()
End If

推荐阅读
coco2冰冰
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有