簡體   English   中英

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

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

我經常在處理從SqlDataAdapters返回的DataRows遇到問題。 當我嘗試使用如下代碼填充對象時:

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

在這種情況下處理DBNull's最佳方法是什么。

可空類型很好,但僅適用於不可開頭的類型。

要使類型“可為空”,請在類型中附加問號,例如:

int? value = 5;

我還建議使用“ as ”關鍵字而不是強制轉換。 您只能在可空類型上使用“as”關鍵字,因此請確保您正在構建已經可以為空的內容(如字符串),或者使用如上所述的可空類型。 對此的推理是

  1. 如果類型可以為空,則如果值為DBNull ,則“ as ”關鍵字將返回null
  2. 盡管只是在某些情況下,比鑄造速度快得多 這對自己是不是一個好足夠的理由來使用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塊來編寫代碼。

但是我會強調,如果可以使用可空類型,那么它們是更好的選擇。 原因在於,對於非可空類型,您將不得不求助於“魔術數字”來表示null。 例如,如果要將列映射到int變量,那么您將如何表示DBNull? 通常你不能使用0,因為0在大多數程序中都有有效含義。 我經常看到人們將DBNull映射到int.MinValue,但這也可能存在問題。 我最好的建議是:

  • 對於數據庫中可以為null的列,請使用可空類型。
  • 對於數據庫中不能為null的列,請使用常規類型。

可以使用可空類型來解決這個問題。 話雖這么說,如果您使用的是較舊版本的框架,或者為那些沒有grok nullable類型的人工作,那么代碼示例就可以解決問題。

添加對System.Data.DataSetExtensions的引用,該引用添加了Linq對查詢數據表的支持。

這將是這樣的:

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

我總是使用If / Else檢查版本,只有三元運算符才能清楚,簡潔,無問題。 將所有內容保留在一行中,包括如果列為空則指定默認值。

因此,假設名為“MyCol”的可為空的Int32列,如果列為null,則返回-99,但如果列不為null,則返回整數值:

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.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);

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

我想這是你正在尋找的最佳實踐

通常在使用DataTables時你必須處理這種情況,其中row字段可以是null或DBNull,通常我會像這樣處理:

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

'as'運算符為無效的強制轉換返回null,比如DBNull為string,'??' 如果第一個為null,則將該項返回到表達式的右側。

您還應該查看擴展方法。 以下是一些處理此場景的示例。

推薦閱讀

您還可以使用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;
}

幾天前,布拉德艾布拉姆斯發布了相關信息http://blogs.msdn.com/brada/archive/2009/02/09/framework-design-guidelines-system-dbnull.aspx

在摘要“AVOID using System.DBNull。Prefer 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值轉換為空字符串。

這樣做非常簡單,但它會增加一些開銷,尤其是在處理大型DataTable時。 如果您有興趣,請查看顯示如何操作的鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM