使用 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)和不使用它有啥区别?