处理DBNull的最好方法是什么?
我经常遇到处理从SqlDataAdapters
返回的DataRows
问题。 当我尝试使用如下代码填充对象时:
DataRow row = ds.Tables[0].Rows[0]; string value = (string)row;
在这种情况下处理DBNull's
最好方法是什么?
可为空的types是好的,但只适用于不可空的types。
为了使一个“可为空”的types在types上附加一个问号,例如:
int? value = 5;
我也build议使用“ as
”关键字来代替转换。 你只能在可空types上使用“as”关键字,所以要确保你正在投射已经可以为null的东西(比如string),或者使用上面提到的可以为null的types。 推理是这样的
- 如果一个types是可空的,那么如果一个值是
DBNull
,则“as
”关键字返回null
。 - 虽然只是在某些情况下,它比铸造稍快 。 这本身并不是一个足够好的理由来使用,而是加上上面的原因是有用的。
我build议做这样的事情
DataRow row = ds.Tables[0].Rows[0]; string value = row as string;
在上面的例子中,如果row
返回为DBNull
,那么value
将变为null
而不是抛出exception。 请注意,如果您的数据库查询更改了返回的列/types, 使用as
会导致您的代码无提示失败 ,并使值简单为null
而不是在返回不正确的数据时抛出相应的exception,因此build议您进行testing以其他方式validation您的查询,以确保您的代码库发展时的数据完整性。
如果您不使用可空types,最好的办法是检查列的值是否为DBNull。 如果它是DBNull,那么将你的引用设置为对于相应的数据types使用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所说的,你可以用每种types的重载转换方法创build一个convert类,所以你不必用if / else块来代码。
但是我会强调,可以使用空types是更好的路线。 原因是,对于不可空的types,你将不得不求助于“幻数”来表示空值。 例如,如果你将一个列映射到一个intvariables,你将如何表示DBNull? 通常你不能使用0,因为0在大多数程序中有一个有效的含义。 我经常看到人们将DBNull映射到int.MinValue,但是这也可能是有问题的。 我最好的build议是这样的:
- 对于数据库中可以为空的列,请使用可为空的types。
- 对于数据库中不能为空的列,请使用常规types。
可空types是为了解决这个问题。 也就是说,如果你使用的是旧版本的框架,或者为那些不支持可空types的人工作,那么代码示例就可以实现。
我总是发现使用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"]);
添加对System.Data.DataSetExtensions
的引用,添加Linq支持查询数据表。
这将是这样的:
string value = ( from row in ds.Tables[0].Rows select row.Field<string>(0) ).FirstOrDefault();
如果您对正在返回结果的查询有控制权,则可以使用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();
但是,这只适用于string,对于其他所有我会使用linq方式或扩展方法。 对于我自己,我写了一个小的扩展方法,检查DBNull,甚至通过Convert.ChangeType(...)
int value = row.GetValueOrDefault<int>("count"); int value = row.GetValueOrDefault<int>("count", 15);
我通常编写自己的ConvertDBNull类来包装内置的Convert类。 如果该值是DBNull,则它将返回null(如果其引用types)或默认值(如果其值types)。 例如: – ConvertDBNull.ToInt64(object obj)
返回Convert.ToInt64(obj)
除非obj是DBNull,在这种情况下它将返回0。
通常在处理DataTable时,你必须处理这种情况,行字段可以是null或DBNull,通常我会这样处理:
string myValue = (myDataTable.Rows[i]["MyDbNullableField"] as string) ?? string.Empty;
'as'操作符为无效转换返回null,如DBNull转换为string,'??' 如果第一个为null,则将该项返回到expression式的右侧。
您也可以使用Convert.IsDBNull(MSDN)进行testing。
出于某种原因,我对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。优先selectNullable”。
这是我的两分钱(未经testing的代码:))
// 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的原因?
你也应该看看扩展方法。 这里有一些例子来处理这个scenerio。
推荐阅读
如果您在期待string时担心获取DBNull,则可以select将DataTable中的所有DBNull值转换为空string。
这样做很简单,但会增加一些开销,特别是在处理大型DataTable时。 检查这个链接 ,显示如何做,如果你有兴趣