【发布时间】:2010-09-06 19:25:34
【问题描述】:
在处理从SqlDataAdapters 返回的DataRows 时,我经常遇到问题。当我尝试使用这样的代码填充对象时:
DataRow row = ds.Tables[0].Rows[0];
string value = (string)row;
在这种情况下,处理DBNull's 的最佳方法是什么。
【问题讨论】:
标签: c# .net sql-server
在处理从SqlDataAdapters 返回的DataRows 时,我经常遇到问题。当我尝试使用这样的代码填充对象时:
DataRow row = ds.Tables[0].Rows[0];
string value = (string)row;
在这种情况下,处理DBNull's 的最佳方法是什么。
【问题讨论】:
标签: c# .net sql-server
可以为空的类型很好,但只适用于一开始就不能为空的类型。
要使类型“可为空”,请在该类型上附加一个问号,例如:
int? value = 5;
我还建议使用“as”关键字而不是强制转换。您只能在可空类型上使用“as”关键字,因此请确保您正在转换已经可以为空的内容(如字符串)或使用上述可空类型。原因是
DBNull,“as”关键字返回null。as 的充分理由,但结合上述原因,它很有用。我建议做这样的事情
DataRow row = ds.Tables[0].Rows[0];
string value = row as string;
在上述情况下,如果row 以DBNull 的形式返回,那么value 将变为null 而不会引发异常。请注意,如果您的数据库查询更改了返回的列/类型,使用as 将导致您的代码静默失败并使值简单null 而不是在出现错误数据时抛出适当的异常返回,因此建议您进行适当的测试,以其他方式验证您的查询,以确保随着代码库的发展而数据完整性。
【讨论】:
如果您不使用可空类型,最好检查列的值是否为 DBNull。如果是 DBNull,则将引用设置为对应数据类型的 null/empty。
DataRow row = ds.Tables[0].Rows[0];
string value;
if (row["fooColumn"] == DBNull.Value)
{
value = string.Empty;
}
else
{
value = Convert.ToString(row["fooColumn"]);
}
正如 Manu 所说,您可以创建一个转换类,其中每个类型都有一个重载的转换方法,因此您不必在代码中添加 if/else 块。
不过,我会强调,如果您可以使用可空类型,则它们是更好的选择。原因是对于不可为空的类型,您将不得不求助于“幻数”来表示空值。例如,如果您将列映射到 int 变量,您将如何表示 DBNull?通常您不能使用 0,因为 0 在大多数程序中具有有效含义。我经常看到人们将 DBNull 映射到 int.MinValue,但这也可能存在问题。我最好的建议是:
可空类型是为了解决这个问题。话虽如此,如果您使用的是旧版本的框架,或者为不了解可空类型的人工作,那么代码示例就可以解决问题。
【讨论】:
添加对System.Data.DataSetExtensions 的引用,这增加了对查询数据表的 Linq 支持。
这可能是这样的:
string value = (
from row in ds.Tables[0].Rows
select row.Field<string>(0) ).FirstOrDefault();
【讨论】:
我总是发现使用 If/Else 检查的版本清晰、简洁且没有问题,仅使用三元运算符。将所有内容保留在一行中,包括在列为空时分配默认值。
因此,假设一个名为“MyCol”的可空 Int32 列,如果该列为空,我们希望返回 -99,但如果该列不为空,则返回整数值:
return row["MyCol"] == DBNull.Value ? -99 : Convert.ToInt32(Row["MyCol"]);
这与上面的 If/Else 获胜者的方法相同 - 但我发现如果您正在从数据读取器读取多个列,那么将所有列读取行一个接一个排列在一起是一个真正的好处,因为更容易发现错误:
Object.ID = DataReader["ID"] == DBNull.Value ? -99 : Convert.ToInt32(DataReader["ID"]);
Object.Name = DataReader["Name"] == DBNull.Value ? "None" : Convert.ToString(DataReader["Name"]);
Object.Price = DataReader["Price"] == DBNull.Value ? 0.0 : Convert.ToFloat(DataReader["Price"]);
【讨论】:
如果您可以控制返回结果的查询,则可以使用 ISNULL() 返回非空值,如下所示:
SELECT
ISNULL(name,'') AS name
,ISNULL(age, 0) AS age
FROM
names
如果您的情况可以容忍这些神奇的值代替 NULL,那么采用这种方法可以解决整个应用程序的问题,而不会弄乱您的代码。
【讨论】:
DBNull 像其他所有东西一样实现 .ToString() 。无需做任何事情。调用对象的 .ToString() 方法,而不是强制转换。
DataRow row = ds.Tables[0].Rows[0];
string value;
if (row["fooColumn"] == DBNull.Value)
{
value = string.Empty;
}
else
{
value = Convert.ToString(row["fooColumn"]);
}
这变成:
DataRow row = ds.Tables[0].Rows[0];
string value = row.ToString()
DBNull.ToString() 返回 string.Empty
我想这是您正在寻找的最佳实践
【讨论】:
值得一提的是,DBNull.Value.ToString() 等于 String.Empty
你可以利用它来发挥你的优势:
DataRow row = ds.Tables[0].Rows[0];
string value = row["name"].ToString();
但是,这仅适用于字符串,对于其他一切我会使用 linq 方式或扩展方法。对于我自己,我编写了一个小扩展方法来检查 DBNull,甚至通过 Convert.ChangeType(...) 进行强制转换
int value = row.GetValueOrDefault<int>("count");
int value = row.GetValueOrDefault<int>("count", 15);
【讨论】:
通常在使用 DataTables 时,您必须处理这种情况,其中行字段可以为 null 或 DBNull,通常我会这样处理:
string myValue = (myDataTable.Rows[i]["MyDbNullableField"] as string) ?? string.Empty;
'as' 运算符为无效转换返回 null,例如 DBNull 到字符串,'??'返回 如果第一个为空,则表达式右侧的项。
【讨论】:
您也可以使用Convert.IsDBNull (MSDN) 进行测试。
【讨论】:
我通常编写自己的 ConvertDBNull 类来包装内置的 Convert 类。如果值是 DBNull,如果它是引用类型,它将返回 null;如果它是值类型,则返回默认值。
例子:
- ConvertDBNull.ToInt64(object obj) 返回 Convert.ToInt64(obj) 除非 obj 是 DBNull,在这种情况下它将返回 0。
【讨论】:
由于某种原因,我在检查 DBNull.Value 时遇到了问题,所以我做了一些稍微不同的事情,并利用了 DataRow 对象中的一个属性:
if (row.IsNull["fooColumn"])
{
value = string.Empty();
}
{
else
{
value = row["fooColumn"].ToString;
}
【讨论】:
Brad Abrams 几天前发布了一些相关的内容 http://blogs.msdn.com/brada/archive/2009/02/09/framework-design-guidelines-system-dbnull.aspx
总结“避免使用 System.DBNull。更喜欢 Nullable。”
这是我的两分钱(未经测试的代码:))
// Or if (row["fooColumn"] == DBNull.Value)
if (row.IsNull["fooColumn"])
{
// use a null for strings and a Nullable for value types
// if it is a value type and null is invalid throw a
// InvalidOperationException here with some descriptive text.
// or dont check for null at all and let the cast exception below bubble
value = null;
}
else
{
// do a direct cast here. dont use "as", "convert", "parse" or "tostring"
// as all of these will swallow the case where is the incorect type.
// (Unless it is a string in the DB and really do want to convert it)
value = (string)row["fooColumn"];
}
还有一个问题...您不使用 ORM 的任何原因?
【讨论】:
如果您担心在期望字符串时获取 DBNull,一种选择是将 DataTable 中的所有 DBNull 值转换为空字符串。
这很简单,但会增加一些开销,尤其是在处理大型数据表时。如果您有兴趣,请查看此link,它显示了如何操作
【讨论】: