简体   繁体   English

处理DBNull的最佳方法是什么?

[英]What is the best way to deal with DBNull's

I frequently have problems dealing with DataRows returned from SqlDataAdapters . 我经常在处理从SqlDataAdapters返回的DataRows遇到问题。 When I try to fill in an object using code like this: 当我尝试使用如下代码填充对象时:

DataRow row = ds.Tables[0].Rows[0];
string value = (string)row;

What is the best way to deal with DBNull's in this type of situation. 在这种情况下处理DBNull's最佳方法是什么。

Nullable types are good, but only for types that are not nullable to begin with. 可空类型很好,但仅适用于不可开头的类型。

To make a type "nullable" append a question mark to the type, for example: 要使类型“可为空”,请在类型中附加问号,例如:

int? value = 5;

I would also recommend using the " as " keyword instead of casting. 我还建议使用“ as ”关键字而不是强制转换。 You can only use the "as" keyword on nullable types, so make sure you're casting things that are already nullable (like strings) or you use nullable types as mentioned above. 您只能在可空类型上使用“as”关键字,因此请确保您正在构建已经可以为空的内容(如字符串),或者使用如上所述的可空类型。 The reasoning for this is 对此的推理是

  1. If a type is nullable, the " as " keyword returns null if a value is DBNull . 如果类型可以为空,则如果值为DBNull ,则“ as ”关键字将返回null
  2. It's ever-so-slightly faster than casting though only in certain cases . 尽管只是在某些情况下,比铸造速度快得多 This on its own is never a good enough reason to use as , but coupled with the reason above it's useful. 这对自己是不是一个好足够的理由来使用as ,但是加上上面是非常有用的原因。

I'd recommend doing something like this 我建议做这样的事情

DataRow row = ds.Tables[0].Rows[0];
string value = row as string;

In the case above, if row comes back as DBNull , then value will become null instead of throwing an exception. 在上面的例子中,如果row返回为DBNull ,则value将变为null而不是抛出异常。 Be aware that if your DB query changes the columns/types being returned, using as will cause your code to silently fail and make values simple null instead of throwing the appropriate exception when incorrect data is returned so it is recommended that you have tests in place to validate your queries in other ways to ensure data integrity as your codebase evolves. 请注意,如果您的数据库查询更改了返回的列/类型,则使用as会导致代码无提示失败并使值简单为null而不是在返回不正确的数据时抛出相应的异常,因此建议您进行测试以其他方式验证您的查询,以确保您的代码库发展时的数据完整性。

If you aren't using nullable types, the best thing to do is check to see if the column's value is DBNull. 如果您没有使用可空类型,最好的办法是检查列的值是否为DBNull。 If it is DBNull, then set your reference to what you use for null/empty for the corresponding datatype. 如果是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"]);
}

As Manu said, you can create a convert class with an overloaded convert method per type so you don't have to pepper your code with if/else blocks. 正如Manu所说,你可以创建一个转换类,每个类型都有一个重载的转换方法,所以你不必用if / else块来编写代码。

I will however stress that nullable types is the better route to go if you can use them. 但是我会强调,如果可以使用可空类型,那么它们是更好的选择。 The reasoning is that with non-nullable types, you are going to have to resort to "magic numbers" to represent null. 原因在于,对于非可空类型,您将不得不求助于“魔术数字”来表示null。 For example, if you are mapping a column to an int variable, how are you going to represent DBNull? 例如,如果要将列映射到int变量,那么您将如何表示DBNull? Often you can't use 0 because 0 has a valid meaning in most programs. 通常你不能使用0,因为0在大多数程序中都有有效含义。 Often I see people map DBNull to int.MinValue, but that could potentially be problematic too. 我经常看到人们将DBNull映射到int.MinValue,但这也可能存在问题。 My best advice is this: 我最好的建议是:

  • For columns that can be null in the database, use nullable types. 对于数据库中可以为null的列,请使用可空类型。
  • For columns that cannot be null in the database, use regular types. 对于数据库中不能为null的列,请使用常规类型。

Nullable types were made to solve this problem. 可以使用可空类型来解决这个问题。 That being said, if you are on an older version of the framework or work for someone who doesn't grok nullable types, the code example will do the trick. 话虽这么说,如果您使用的是较旧版本的框架,或者为那些没有grok nullable类型的人工作,那么代码示例就可以解决问题。

Add a reference to System.Data.DataSetExtensions , that adds Linq support for querying data tables. 添加对System.Data.DataSetExtensions的引用,该引用添加了Linq对查询数据表的支持。

This would be something like: 这将是这样的:

string value = (
    from row in ds.Tables[0].Rows
    select row.Field<string>(0) ).FirstOrDefault();

I always found it clear, concise, and problem free using a version of the If/Else check, only with the ternary operator. 我总是使用If / Else检查版本,只有三元运算符才能清楚,简洁,无问题。 Keeps everything on one row, including assigning a default value if the column is null. 将所有内容保留在一行中,包括如果列为空则指定默认值。

So, assuming a nullable Int32 column named "MyCol", where we want to return -99 if the column is null, but return the integer value if the column is not null: 因此,假设名为“MyCol”的可为空的Int32列,如果列为null,则返回-99,但如果列不为null,则返回整数值:

return row["MyCol"] == DBNull.Value ? -99 : Convert.ToInt32(Row["MyCol"]);

It is the same method as the If/Else winner above - But I've found if you're reading multiple columns in from a datareader, it's a real bonus having all the column-read lines one under another, lined up, as it's easier to spot errors: 它与上面的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"]);

If you have control of the query that is returning the results, you can use ISNULL() to return non-null values like this: 如果您可以控制返回结果的查询,则可以使用ISNULL()返回非空值,如下所示:

SELECT 
  ISNULL(name,'') AS name
  ,ISNULL(age, 0) AS age
FROM 
  names

If your situation can tolerate these magic values to substitute for NULL, taking this approach can fix the issue through your entire app without cluttering your code. 如果您的情况可以容忍这些魔术值替换为NULL,那么采用这种方法可以解决整个应用程序中的问题,而不会使代码混乱。

It is worth mentioning, that DBNull.Value.ToString() equals String.Empty 值得一提的是, DBNull.Value.ToString()等于String.Empty

You can use this to your advantage: 你可以利用这个优势:

DataRow row = ds.Tables[0].Rows[0];
string value = row["name"].ToString();

However, that only works for Strings, for everything else I would use the linq way or a extension method. 但是,这仅适用于字符串,对于其他所有我将使用linq方式或扩展方法。 For myself, I have written a little extension method that checks for DBNull and even does the casting via Convert.ChangeType(...) 对于我自己,我写了一个小的扩展方法,检查DBNull甚至通过Convert.ChangeType(...)

int value = row.GetValueOrDefault<int>("count");
int value = row.GetValueOrDefault<int>("count", 15);

DBNull implements .ToString() like everything else. DBNull像其他一样实现.ToString()。 No need to do anything. 无需做任何事情。 Instead of the hard cast, call the object's .ToString() method. 而不是硬强制转换,调用对象的.ToString()方法。

DataRow row = ds.Tables[0].Rows[0];
string value;

if (row["fooColumn"] == DBNull.Value)
{
   value = string.Empty;
}
else 
{
   value = Convert.ToString(row["fooColumn"]);
}

this becomes: 这成了:

DataRow row = ds.Tables[0].Rows[0];
string value = row.ToString()

DBNull.ToString() returns string.Empty DBNull.ToString()返回string.Empty

I would imagine this is the best practice you're looking for 我想这是你正在寻找的最佳实践

Often when working with DataTables you have to deal with this cases, where the row field can be either null or DBNull, normally I deal with that like this: 通常在使用DataTables时你必须处理这种情况,其中row字段可以是null或DBNull,通常我会像这样处理:

string myValue = (myDataTable.Rows[i]["MyDbNullableField"] as string) ?? string.Empty;

The 'as' operator returns null for invalid cast's, like DBNull to string, and the '??' 'as'运算符为无效的强制转换返回null,比如DBNull为string,'??' returns the term to the right of the expression if the first is null. 如果第一个为null,则将该项返回到表达式的右侧。

You should also look at the extension methods. 您还应该查看扩展方法。 Here are some examples to deal with this scenerio. 以下是一些处理此场景的示例。

Recommended read 推荐阅读

您还可以使用Convert.IsDBNull(MSDN)进行测试。

I usually write my own ConvertDBNull class that wraps the built-in Convert class. 我通常编写自己的ConvertDBNull类来包装内置的Convert类。 If the value is DBNull it will return null if its a reference type or the default value if its a value type. 如果值为DBNull,则它将返回null(如果是引用类型)或默认值(如果是值类型)。 Example: - ConvertDBNull.ToInt64(object obj) returns Convert.ToInt64(obj) unless obj is DBNull in which case it will return 0. 示例: - ConvertDBNull.ToInt64(object obj)返回Convert.ToInt64(obj)除非obj是DBNull,在这种情况下它将返回0。

For some reason I've had problems with doing a check against DBNull.Value, so I've done things slightly different and leveraged a property within the DataRow object: 出于某种原因,我在对DBNull.Value进行检查时遇到了问题,所以我做了一些稍微不同的事情,并在DataRow对象中利用了一个属性:

if (row.IsNull["fooColumn"])
{
   value = string.Empty();
}
{
else
{
   value = row["fooColumn"].ToString;
}

Brad Abrams posted something related just a couple of days ago http://blogs.msdn.com/brada/archive/2009/02/09/framework-design-guidelines-system-dbnull.aspx 几天前,布拉德艾布拉姆斯发布了相关信息http://blogs.msdn.com/brada/archive/2009/02/09/framework-design-guidelines-system-dbnull.aspx

In Summary "AVOID using System.DBNull. Prefer Nullable instead." 在摘要“AVOID using System.DBNull。Prefer Nullable”。

And here is my two cents (of untested code :) ) 这是我的两分钱(未经测试的代码:))

// 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"];
}

And one question... Any reason you are not using an ORM? 还有一个问题......你有没有使用ORM的原因?

If you are concerned with getting DBNull when expecting strings, one option is to convert all the DBNull values in the DataTable into empty string. 如果您担心在期望字符串时获取DBNull,则可以选择将DataTable中的所有DBNull值转换为空字符串。

It is quite simple to do it but it would add some overhead especially if you are dealing with large DataTables. 这样做非常简单,但它会增加一些开销,尤其是在处理大型DataTable时。 Check this link that shows how to do it if you are interested 如果您有兴趣,请查看显示如何操作的链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM