使用 SqlDataReader.IsDBNull 时使用列名

Posted

技术标签:

【中文标题】使用 SqlDataReader.IsDBNull 时使用列名【英文标题】:Using column name when using SqlDataReader.IsDBNull 【发布时间】:2013-10-04 05:26:50 【问题描述】:

你好,我有这个从 SQL DB 读取数据的代码。

我不知道应该如何编辑它,以便可以使用原始列名而不是列索引。

string query = "SELECT * FROM zajezd WHERE event='" + thisrow+ "' AND year='" + klientClass.Year() + "'";
SqlCommand cmd= new SqlCommand(query, spojeni);
spojeni.Open();
SqlDataReader read= cmd.ExecuteReader();


if (read.Read())

    maskedTextBox2.Text = read.IsDBNull(24) ? 
        string.Empty : 
        read.GetDateTime(24).ToString("MM/dd/yyyy");

提前致谢。

【问题讨论】:

所以您使用的是 SELECT * 并且您所追求的列是第 24 列?这是一种糟糕的编码方式——当有人更改表格时会发生什么(例如,在 1-23 之间的某处添加一列)?您可能会收到错误消息,或者您可能碰巧获得了不同的日期时间列而没有注意到。另外,你喜欢 SQL 注入吗?请使用参数化查询。像这样的动态 SQL 就是每天网络上都有如此多的 SQL 注入攻击的原因。 @NathanKoop 在您的编辑中添加了右括号。可能您是对的,但正如您从下面的 cmets 中看到的那样,这为 OP 使用的查询提供了非常具体的含义。如果 OP 只需要一列,则可能有更好的方法。 @Steve 对延迟回复感到抱歉。我没有看到您引用的评论,但我已经删除了卷曲。不管它是否存在,对我来说都不是什么大问题:-) @NathanKoop 我们正在讨论一个简单的 ExecuteScalar 是否足以满足 OP 的需求。当您必须从一行中仅检索一列时,ExecuteScalar 是首选方法。缺少的大括号已被(由我)视为 OP 在读取单个字段之后还有其他代码的标志,在这种情况下,ExecuteScalar 不是正确的工具。有了右括号,我的答案仍然有效,但 ExecuteScalar 更有意义,正如 Scott Chamberlain 在我的回答中指出的那样。对我来说也没什么大不了的。 【参考方案1】:

您正在寻找SqlDataReader.GetOrdinal

根据 MSDN

根据列名获取列序号。

if (read.Read())

   int colIndex = read.GetOrdinal("MyColumnName");
   maskedTextBox2.Text = read.IsDBNull(colIndex) ? 
                  string.Empty : 
                  read.GetDateTime(colIndex).ToString("MM/dd/yyyy");


附带说明,您的查询对 sql 注入开放。不要使用字符串连接来构建 sql 命令,而是使用参数化查询

  string query = "SELECT * FROM zajezd WHERE event=@p1 AND year=@p2";
  using(SqlCommand cmd= new SqlCommand(query, spojeni))
  
     spojeni.Open();
     cmd.Parameters.AddWithValue("@p1",thisrow);
     cmd.Parameters.AddWithValue("@p2",klientClass.Year().ToString());
     using(SqlDataReader read= cmd.ExecuteReader())
     
       ......
     
  

【讨论】:

我没有投反对票,但如果我这样做,我会在循环之外进行读取。编辑:刚刚看到这是一个if 语句而不是循环。我猜他应该改用 ExecuteScaler。 我们假设 OP 只需要一列,但这无法从发布的不完整代码中验证,但是如果 OP 真的只需要一条记录中的一列,我同意整个查询应该改写 很对,没想到。 不是我的 d/v,但我确实对类似的风格问题投了反对票,我没有明确声明 OP 不应该使用这种风格的 SQL。这可能是原因,我看到你现在已经添加了它。 出于性能原因,Microsoft 现在建议您在读取循环外调用 GetOrdinal,并且仅在循环内调用 GetDateTime(int)【参考方案2】:

我会尝试(string)(reader["ColumnName"] == DBNull.Value ? "" : reader["ColumnName"]); 按列名来做。

【讨论】:

这就是我们所做的并且工作正常 (string)(reader["ColumnName"] == DBNull.Value ? "" : reader["ColumnName"]); 嗯,这是正确的,但你为什么不添加到你的答案呢? 我不知道他想要的不是什么大不了的,尽管我会编辑它并且你的为他工作。【参考方案3】:

您可以使用GetOrdinal 创建自己的IsDBNull(string name) 扩展方法。

[DebuggerStepThrough]
public static class SqlDataReaderExtensions

    /// <summary>Gets a value that indicates whether the column contains non-existent or missing values.</summary>
    /// <param name="name">The name of the column. </param>
    /// <returns> <see langword="true" /> if the specified column value is equivalent to <see cref="T:System.DBNull" />; otherwise <see langword="false" />.</returns>
    /// <exception cref="T:System.IndexOutOfRangeException">The name specified is not a valid column name. </exception>
    public static bool IsDBNull(this SqlDataReader reader, string name)
    
        int columnOrdinal = reader.GetOrdinal(name);
        return reader.IsDBNull(columnOrdinal);
    


// Usage
if (read.Read())

    maskedTextBox2.Text = read.IsDBNull("MyColumnName") ? 
                  string.Empty : 
                  read.GetDateTime("MyColumnName").ToString("MM/dd/yyyy");


【讨论】:

【参考方案4】:

您可以像这样使用带有 isnull 属性的 GetOrdinal:

SqlDataReader reader = await cmd.ExecuteReaderAsync();
model.fieldName = reader.IsDBNull(reader.GetOrdinal("fieldName")) ? false : Convert.ToBoolean(reader["fieldName"]);

【讨论】:

以上是关于使用 SqlDataReader.IsDBNull 时使用列名的主要内容,如果未能解决你的问题,请参考以下文章

测试使用

第一篇 用于测试使用

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”