2008-08-25 22 views
41

Tôi thường gặp vấn đề khi giao dịch với DataRows được trả về từ SqlDataAdapters. Khi tôi cố gắng điền vào một đối tượng sử dụng mã như sau:Cách tốt nhất để đối phó với DBNull là

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

Cách tốt nhất để xử lý DBNull's trong loại tình huống này là gì.

+0

đóng: [hiệu quả nhất-cách-to-check-cho-dbnull-và-sau đó-gán-cho-một biến] (http://stackoverflow.com/questions/221582/most-efficient-way -to-check-for-dbnull-và-sau đó-gán-cho-một-biến) – nawfal

Trả lời

34

Loại dễ vỡ là tốt, nhưng chỉ dành cho các loại không có giá trị để bắt đầu.

Để thực hiện một loại "nullable" thêm một dấu hỏi to go, ví dụ:

int? value = 5; 

Tôi cũng muốn giới thiệu cách sử dụng "as" từ khóa thay vì đúc. Bạn chỉ có thể sử dụng từ khóa "as" trên các loại nullable, vì vậy hãy chắc chắn rằng bạn đang truyền những thứ đã được vô hiệu hóa (như chuỗi) hoặc bạn sử dụng các kiểu nullable như đã đề cập ở trên. Lý do cho điều này là

  1. Nếu một loại là nullable, các "as" từ khóa trả null nếu một giá trị là DBNull.
  2. Đó là ever-so-slightly faster than casting mặc dù only in certain cases. Điều này ngày của riêng mình là không bao giờ là một lý do đủ tốt để sử dụng as, nhưng cùng với lý do trên nó rất hữu ích.

tôi khuyên bạn nên làm một cái gì đó như thế này

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

Trong trường hợp trên, nếu row trở lại như DBNull, sau đó sẽ trở thành valuenull thay vì ném một ngoại lệ. Lưu ý rằng nếu truy vấn DB của bạn thay đổi các cột/kiểu được trả về, sử dụng as sẽ khiến mã của bạn không hoạt động âm thầm và làm cho các giá trị đơn giản null thay vì ném ngoại lệ thích hợp khi dữ liệu không chính xác được trả về. tại chỗ để xác thực truy vấn của bạn theo những cách khác để đảm bảo tính toàn vẹn dữ liệu khi codebase của bạn phát triển.

6

Thêm tham chiếu đến System.Data.DataSetExtensions, thêm hỗ trợ LINQ để truy vấn bảng dữ liệu.

Đây sẽ là một cái gì đó như:

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

+1: câu trả lời này nên được upvoted hơn và có lẽ cũng được chấp nhận - chắc chắn row.Field ("column_name") là hiệu quả nhất , giải pháp đọc được và sạch sẽ để xử lý null (DBNull.Value). Ngoài ra còn có row.SetField ("column_name", giá trị) để ghi giá trị vào hàng. –

2

Tôi thường viết lớp ConvertDBNull của riêng tôi mà kết thúc tốt đẹp được xây dựng trong lớp Chuyển đổi. Nếu giá trị là DBNull nó sẽ trả về null nếu một kiểu tham chiếu của nó hoặc giá trị mặc định nếu nó là một kiểu giá trị. Ví dụ: - ConvertDBNull.ToInt64(object obj) lợi nhuận Convert.ToInt64(obj) trừ khi obj là DBNull trong trường hợp này nó sẽ trở lại 0.

21

Nếu bạn không sử dụng các loại nullable, điều tốt nhất để làm là kiểm tra xem giá trị của cột là DBNull. Nếu nó là DBNull, sau đó thiết lập tham chiếu của bạn đến những gì bạn sử dụng cho null/empty cho datatype tương ứng.

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

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

Như Manu nói, bạn có thể tạo ra một lớp chuyển đổi với một phương pháp chuyển đổi quá tải cho mỗi loại, do đó bạn không cần phải tiêu mã của bạn với if/else khối.

Tuy nhiên, tôi sẽ nhấn mạnh rằng các loại nullable là tuyến đường tốt hơn để đi nếu bạn có thể sử dụng chúng. Lý do là với các kiểu không nullable, bạn sẽ phải dùng đến "các số ma thuật" để biểu diễn null. Ví dụ, nếu bạn đang ánh xạ một cột tới một biến int, bạn sẽ đại diện cho DBNull như thế nào? Thông thường bạn không thể sử dụng 0 vì 0 có ý nghĩa hợp lệ trong hầu hết các chương trình. Thường thì tôi thấy mọi người bản đồ DBNull thành int.MinValue, nhưng điều đó cũng có thể có vấn đề. Lời khuyên tốt nhất của tôi là:

  • Đối với các cột có thể rỗng trong cơ sở dữ liệu, hãy sử dụng các loại có thể vô hiệu.
  • Đối với các cột không thể rỗng trong cơ sở dữ liệu, hãy sử dụng các loại thông thường.

Loại dễ vỡ được thực hiện để giải quyết vấn đề này. Điều đó đang được nói, nếu bạn đang ở trên một phiên bản cũ của khuôn khổ hoặc làm việc cho một người không grok loại nullable, ví dụ mã sẽ làm các trick.

+0

Dòng nếu (hàng ["fooColumn"] == DBNull.Value) hoạt động, nhưng không chính xác. Nó không được định nghĩa DBNull.Value nên được thực hiện như là một mẫu Singleton. Một dòng tốt hơn sẽ là: if (row ["fooColumn"] là DBNull) – doekman

+0

@doekman - Trên thực tế, DBNull * là * một lớp singleton. Để báo MSDN: "DBNull là một lớp singleton, có nghĩa là chỉ có [DBNull.Value] thể hiện của lớp này có thể tồn tại." http://msdn.microsoft.com/en-us/library/system.dbnull.value.aspx – Greg

+2

Nếu bạn muốn xử lý dbnull như hàng chuỗi rỗng ["fooColumn"]. ToString() sẽ làm điều đó. – samir105

1

Đối với một số lý do tôi đã có vấn đề với làm một kiểm tra chống DBNull.Value, vì vậy tôi đã làm những điều hơi khác nhau và thừa hưởng một tài sản trong phạm vi đối tượng DataRow:

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

Nếu bạn có quyền kiểm soát các truy vấn được trả lại kết quả, bạn có thể sử dụng ISNULL() để trở về giá trị khác null như thế này:

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

Nếu tình hình của bạn có thể chịu đựng những giá trị kỳ diệu để thay thế cho NULL, dùng phương pháp này có thể khắc phục vấn đề thông qua toàn bộ ứng dụng của bạn mà không làm lộn xộn mã.

8

Tôi luôn thấy nó rõ ràng, súc tích và không có vấn đề bằng cách sử dụng phiên bản kiểm tra Nếu/Khác, chỉ với toán tử bậc ba. Giữ mọi thứ trên một hàng, bao gồm gán giá trị mặc định nếu cột là null.

Vì vậy, giả sử một cột Int32 nullable tên "MyCol", nơi mà chúng tôi muốn trở lại -99 nếu cột là null, nhưng trả về giá trị số nguyên nếu cột không là null:

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

Đó là phương pháp tương tự như người thắng cuộc If/Else ở trên - Nhưng tôi đã tìm thấy nếu bạn đang đọc nhiều cột từ một nhà quản lý dữ liệu, đó là tiền thưởng thực sự có tất cả các dòng đọc cột dưới một cột khác, xếp hàng, vì nó dễ dàng hơn lỗi tại chỗ:

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"]); 
0

Nếu bạn quan tâm đến việc nhận DBNull khi mong đợi g string, một tùy chọn là chuyển đổi tất cả các giá trị DBNull trong DataTable thành chuỗi rỗng.

Nó khá đơn giản để làm điều đó nhưng nó sẽ thêm một số chi phí đặc biệt là nếu bạn đang đối phó với DataTables lớn. Kiểm tra link này cho thấy làm thế nào để làm điều đó nếu bạn quan tâm

+0

Đôi khi điều quan trọng là phải biết sự khác biệt giữa một chuỗi Null và một chuỗi rỗng. Ví dụ thiết lập một giá trị cho một chuỗi rỗng như trái ngược với một giá trị không bao giờ được thiết lập. – Mykroft

1

Brad Abrams gửi một cái gì đó liên quan đến chỉ một vài ngày trước http://blogs.msdn.com/brada/archive/2009/02/09/framework-design-guidelines-system-dbnull.aspx

Trong Summary "AVOID sử dụng System.DBNull. Thích Nullable thay thế."

Và đây là hai xu của tôi (mã chưa được kiểm tra :))

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

Và một câu hỏi ... Bất kỳ lý do bạn không sử dụng một ORM?

+0

Hiện tại, chúng tôi đang sử dụng ORM. Tại thời điểm chúng tôi không phải là – Mykroft

+0

* Bất kỳ lý do nào bạn không sử dụng ORM? * Tôi sử dụng ADO thô.NET cho hiệu suất. Cố gắng hợp nhất 1 triệu bản ghi với EF hoặc NH. –

3

DBNull thực hiện ToString() .. giống như mọi thứ khác Không cần phải làm bất cứ điều gì Thay vì các diễn viên cứng, gọi phương thức của đối tượng ToString()

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

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

này trở thành:.

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

DBNull.ToString() trả về string.Empty

tôi sẽ tưởng tượng này là sự thực hành tốt nhất mà bạn đang tìm kiếm

+0

Điều này không hoạt động nếu giá trị không phải là một chuỗi mặc dù. Tôi đang tìm kiếm một câu trả lời chung. – Mykroft

+0

Tôi đồng ý vâng nó là tốt nếu nó dây giữ nó đơn giản nhưng các loại khác sẽ không làm việc ví dụ tryng để làm bool.Parse (hàng ["fooColumn"]. ToString()). – PeteT

1

Bạn cũng nên xem xét các phương pháp khuyến nông. Here là một số ví dụ để xử lý scenerio này.

Đề xuất read

3

Điều đáng nói, rằng DBNull.Value.ToString() bằng String.Empty

Bạn có thể sử dụng điều này để lợi thế của bạn:

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

Tuy nhiên, đó chỉ hoạt động cho Strings, cho tất cả mọi thứ khác tôi sẽ sử dụng cách linq hoặc phương pháp mở rộng. Đối với bản thân mình, tôi đã viết một phương pháp mở rộng chút để kiểm tra cho DBNull và thậm chí hiện đúc qua Convert.ChangeType(...)

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

Thông thường khi làm việc với DataTables bạn phải đối phó với trường hợp này, nơi mà các lĩnh vực hàng có thể là null hoặc DBNull, bình thường tôi đối phó với điều đó như thế này:

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

các 'là' điều hành trả về null cho hợp lệ cast, như DBNull để chuỗi, và '??' trả về cụm từ ở bên phải của biểu thức nếu cụm từ đầu tiên là rỗng.

Các vấn đề liên quan