如何检查SqlDataReader
对象中是否存在列?在我的数据访问层中,我创建了一个方法,为多个存储过程调用构建相同的对象.其中一个存储过程具有另一个列,其他存储过程不使用该列.我想修改方法以适应每个场景.
我的应用程序是用C#编写的.
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; } }
使用这个布尔函数要好得多:
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; }
我认为你最好的选择是在你的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方法.
在一行中,在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));
以下是Jasmin的想法的工作示例:
var cols = r.GetSchemaTable().Rows.Cast().Select (row => row["ColumnName"] as string).ToList(); if (cols.Contains("the column name")) { }
这对我有用:
bool hasColumnName = reader.GetSchemaTable().AsEnumerable().Any(c => c["ColumnName"] == "YOUR_COLUMN_NAME");
以下内容很简单,对我有用:
bool hasMyColumn = (reader.GetSchemaTable().Select("ColumnName = 'MyColumnName'").Count() == 1);
如果您阅读了这个问题,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().Columns
DataReader返回BS列:
我为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