我如何检查看看一个列是否存在于一个SqlDataReader对象?在我的数据访问层,我创建了一个为多个存储过程调用构建相同对象的方法。其中一个存储过程具有其他存储过程不使用的附加列。我想修改方法以适应各种情况。

我的应用程序是用c#编写的。


当前回答

以下是Jasmine给出的解决方案。(还有一个,虽然很简单!)

reader.GetSchemaTable().Select("ColumnName='MyCol'").Length > 0;

其他回答

这段代码纠正了Levitikon在他们的代码中遇到的问题: (改编自:[1]:http://msdn.microsoft.com/en-us/library/system.data.datatablereader.getschematable.aspx)

public List<string> GetColumnNames(SqlDataReader r)
{
    List<string> ColumnNames = new List<string>();
    DataTable schemaTable = r.GetSchemaTable();
    DataRow row = schemaTable.Rows[0];
    foreach (DataColumn col in schemaTable.Columns)
    {
        if (col.ColumnName == "ColumnName") 
        { 
            ColumnNames.Add(row[col.Ordinal].ToString()); 
            break; 
        }
    }
    return ColumnNames;
}

获取所有那些无用的列名,而不是从表中获取列名的原因是…… 是因为您正在获取模式列的名称(即schema表的列名)

注意:这似乎只返回第一列的名称…

EDIT:返回所有列名称的修正代码,但不能使用SqlDataReader来完成

public List<string> ExecuteColumnNamesReader(string command, List<SqlParameter> Params)
{
    List<string> ColumnNames = new List<string>();
    SqlDataAdapter da = new SqlDataAdapter();
    string connection = ""; // your sql connection string
    SqlCommand sqlComm = new SqlCommand(command, connection);
    foreach (SqlParameter p in Params) { sqlComm.Parameters.Add(p); }
    da.SelectCommand = sqlComm;
    DataTable dt = new DataTable();
    da.Fill(dt);
    DataRow row = dt.Rows[0];
    for (int ordinal = 0; ordinal < dt.Columns.Count; ordinal++)
    {
        string column_name = dt.Columns[ordinal].ColumnName;
        ColumnNames.Add(column_name);
    }
    return ColumnNames; // you can then call .Contains("name") on the returned collection
}

我认为你最好的办法是在你的DataReader上预先调用GetOrdinal("columnName"),并在列不存在的情况下捕获indexoutofranceexception。

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

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

Edit

好吧,这篇文章最近开始获得一些反对票,我不能删除它,因为它是公认的答案,所以我将更新它,并(我希望)尝试证明使用异常处理作为控制流的合理性。

另一种实现方法是循环遍历DataReader中的每个字段,并对要查找的字段名进行不区分大小写的比较。这将工作得非常好,实际上可能会比我上面的方法更好。当然,我绝不会在性能有问题的循环中使用上述方法。

我可以想到一种情况,在这种情况下,try/GetOrdinal/catch方法可以工作,而循环则不行。然而,这完全是一个假设的情况,所以这是一个非常站不住脚的理由。不管怎样,耐心听我说,看看你怎么想。

假设有一个数据库允许您对表中的列进行“别名”。想象一下,我可以定义一个表,它的列名为“EmployeeName”,但也给它一个别名“EmpName”,对其中任何一个名称进行选择都将返回该列中的数据。跟得上吗?

现在假设有一个ADO。NET提供程序,他们为它编写了一个IDataReader实现,其中考虑了列别名。

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

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

脆弱的吗?当然。但值得一想。老实说,我更希望在IDataRecord上有一个“官方的”HasColumn方法。

以下是Jasmine给出的解决方案。(还有一个,虽然很简单!)

reader.GetSchemaTable().Select("ColumnName='MyCol'").Length > 0;

下面是接受答案的一行LINQ版本:

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

对于这个简单的问题,我建议使用try{} catch{}。但是,我不建议在catch中处理异常。

try 
{
  if (string.IsNullOrEmpty(reader["Name"].ToString())) 
  {
    name = reader["Name"].ToString();
  }
}
catch
{
  //Do nothing
}