Không thể truyền đối tượng kiểu 'System.DBNull' thành kiểu 'System.String`


109

Tôi gặp lỗi ở trên trong ứng dụng của mình. Đây là mã gốc

public string GetCustomerNumber(Guid id)
{
     string accountNumber = 
          (string)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidmyApp, 
                          CommandType.StoredProcedure, 
                          "GetCustomerNumber", 
                          new SqlParameter("@id", id));
     return accountNumber.ToString();
 }

Tôi đã thay thế bằng

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));
    if (accountNumber is System.DBNull)
    {
       return string.Empty;
    }
    else
    {
       return accountNumber.ToString();
    }
}

Có cách nào tốt hơn để giải quyết vấn đề này không?


2
bạn thực sự cần nhìn vào câu trả lời @ kiềm chế, sẽ giúp bạn tiết kiệm rất nhiều thời gian trong thời gian dài
m roman

Câu trả lời:


90

Có thể sử dụng mẫu ngắn hơn:

return (accountNumber == DBNull.Value) ? string.Empty : accountNumber.ToString()

CHỈNH SỬA: Chưa chú ý đến ExecuteScalar. Nó thực sự trả về null nếu trường không có trong kết quả trả về. Vì vậy, hãy sử dụng thay thế:

return (accountNumber == null) ? string.Empty : accountNumber.ToString() 

3
Điều đó sẽ không hoạt động - "accountNumber" không phải là một giá trị cơ sở dữ liệu mà là một phiên bản "đối tượng" Plain Old .NET cũ thông thường - bạn cần kiểm tra giá trị "null" bình thường. DBNull.Value sẽ hoạt động cho SqlDataReader hoặc SqlParameter - nhưng không hoạt động với đối tượng này ở đây.
marc_s

Bạn nói đúng, mình bắt đầu tối ưu phần kiểm tra điều kiện, chưa nhìn dòng bao giờ. Mea culpa.
Người dùng

Có lỗi đánh máy trong bài đăng của bạn mà tôi thực sự không thể chỉnh sửa vì bản chỉnh sửa yêu cầu phải thay đổi 6 ký tự. Cần thay đổi một ai đó accountNumber.TosString () để accountNumber.ToString ()
Eric

@marc_s Tùy thuộc vào bố cục db / truy vấn, bạn cần kiểm tra một trong hai hoặc thậm chí cả hai. Nếu WHERE không khớp với bất kỳ hàng nào, bạn sẽ nhận được null, nếu hàng đã chọn có NULLtrong cột đó, giá trị trả về là System.DBNull.
Alexander

Trong trường hợp đầu tiên @Alexander đề cập -không khớp với bất kỳ hàng nào- bạn có thể dựa vào Convert.ToString hoặc bất kỳ phương thức Chuyển đổi nào khác nếu bạn hài lòng với giá trị mà chúng trả về khi chuyển đổi từ null: chuỗi trống cho chuỗi, 0 cho giá trị số, false cho boolean, MinValue cho DateTime ... msdn.microsoft.com/en-us/library/vstudio/…
Jaime

199

Với một hàm chung đơn giản, bạn có thể thực hiện điều này rất dễ dàng. Chỉ cần làm điều này:

return ConvertFromDBVal<string>(accountNumber);

sử dụng chức năng:

public static T ConvertFromDBVal<T>(object obj)
{
    if (obj == null || obj == DBNull.Value)
    {
        return default(T); // returns the default value for the type
    }
    else
    {
        return (T)obj;
    }
}

1
Vâng, một chức năng như thế này là giải pháp thực tế duy nhất. Bất kỳ loại logic nội dòng nào sẽ không thành công sau khi bạn đã sao chép và dán nó hàng nghìn lần. :-)
Christian Hayter

3
này làm việc sẽ không nếu bạn cố gắng chuyển đổi từ 1 tới bool (Convert.ToBoolean (1) hoạt động tốt tho)
m roman

@roman: như vậy thì chúng ta sẽ muốn có một kiểm tra bổ sung (trước khi kiểm tra cho null) rằng séc cho một loại boolean ...
IAbstract

1
Nếu bạn muốn hoặc cần sử dụng các chức năng Chuyển đổi, thì chức năng này không hoạt động. Có một số tình huống mà bạn có thể muốn chuyển đổi sang một dàn diễn viên rõ ràng. @romanm đã lưu ý một trong số họ. Một vấn đề khác là khi bạn làm việc với số thập phân và quan tâm đến các cơ chế làm tròn khác nhau mà Convert.ToInt32 và (int) sử dụng. Các cựu viên đạn với giá trị thậm chí gần nhất, trong khi các diễn viên rõ ràng chỉ truncates giá trị: stackoverflow.com/questions/1608801/... Nếu có thể, tôi sẽ loại bỏ NULLs từ sự pha trộn, sử dụng T-SQL ISNULL chức năng
Jaime

2
@Jaime Hàm này được cho là hoạt động giống như một kiểu truyền ngầm từ kiểu dữ liệu SQL sang kiểu dữ liệu C # /. NET. Nếu bạn có nhu cầu về một kiểu truyền rõ ràng, đừng sử dụng hàm này - hãy làm điều đó một cách rõ ràng.
kiềm chế

17

ExecuteScalar sẽ trả về

  • null nếu không có tập hợp kết quả
  • nếu không thì cột đầu tiên của hàng đầu tiên của tập kết quả, có thể là DBNull.

Nếu bạn biết rằng cột đầu tiên của tập kết quả là một chuỗi, thì để bao gồm tất cả các cơ sở, bạn cần kiểm tra cả null và DBNull. Cái gì đó như:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null) ? String.Empty : accountNumber.ToString();

Đoạn mã trên dựa trên thực tế là DBNull.ToString trả về một chuỗi rỗng.

Nếu accountNumber là một loại khác (giả sử là số nguyên), thì bạn cần phải rõ ràng hơn:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null || Convert.IsDBNull(accountNumber) ?     
         (int) accountNumber : 0;

Nếu bạn biết chắc rằng tập kết quả của mình sẽ luôn có ít nhất một hàng (ví dụ: CHỌN COUNT (*) ...), thì bạn có thể bỏ qua việc kiểm tra giá trị rỗng.

Trong trường hợp của bạn, thông báo lỗi "Không thể truyền đối tượng kiểu 'System.DBNull' sang loại 'System.String`" cho biết cột đầu tiên của tập kết quả của bạn là giá trị DBNUll. Đây là từ ép kiểu thành chuỗi trên dòng đầu tiên:

string accountNumber = (string) ... ExecuteScalar(...);

Nhận xét của Marc_s rằng bạn không cần kiểm tra DBNull.Value là sai.


tập kết quả của tôi không phải lúc nào cũng trả về một hàng.
Saif Khan

6

Bạn có thể sử dụng toán tử liên kết null của C #

return accountNumber ?? string.Empty;

-1: Điều đó sẽ không biên dịch: phương thức trả về một chuỗi và accountNumber là một đối tượng.
Joe

2
trả về Cmd.ExecuteScalar (). ToString () ?? String.Empty;
Chaitanya

. trở Cmd.ExecuteScalar () ToString () đã làm công việc cho tôi
Taran

3

Có một cách khác để giải quyết vấn đề này. Làm thế nào về việc sửa đổi thủ tục cửa hàng của bạn? bằng cách sử dụng hàm sql ISNULL (trường của bạn, ""), bạn có thể trả về chuỗi trống nếu giá trị trả về là null.

Sau đó, bạn có mã sạch của mình như phiên bản gốc.


3

Đây là phương pháp chung mà tôi sử dụng để chuyển đổi bất kỳ đối tượng nào có thể là DBNull.

public static T ConvertDBNull<T>(object value, Func<object, T> conversionFunction)
{
    return conversionFunction(value == DBNull.Value ? null : value);
}

sử dụng:

var result = command.ExecuteScalar();

return result.ConvertDBNull(Convert.ToInt32);

ngắn hơn:

return command
    .ExecuteScalar()
    .ConvertDBNull(Convert.ToInt32);

2

Tôi cho rằng bạn có thể làm như thế này:

string accountNumber = DBSqlHelperFactory.ExecuteScalar(...) as string;

Nếu accountNumber là null nghĩa là nó không phải là DBNull string :)


Hoặc return (accountNumber as string) ?? string.Empty;, với accountNumber vẫn là một object. Nếu bạn muốn giữ cuộc gọi cơ sở dữ liệu của mình trên đường riêng của nó.
Brian

1

String.Concat biến đổi các giá trị DBNull và null thành một chuỗi rỗng.

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));

    return String.Concat(accountNumber);

 }

Tuy nhiên, tôi nghĩ rằng bạn mất một số thứ về khả năng hiểu mã


1
Điều gì xảy ra nếu bạn viết return "" + accountNumber;?
Zev Spitz

0

Vì tôi nhận được một phiên bản không phải là null và nếu tôi so sánh với DBNULL, tôi đã nhận được lỗi Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull'và nếu tôi cố gắng thay đổi để so sánh với NULL, nó chỉ đơn giản là không hoạt động (vì DBNull là một đối tượng) ngay cả khi đó là câu trả lời được chấp nhận.

Tôi quyết định chỉ sử dụng từ khóa 'là'. Vì vậy, kết quả rất dễ đọc:

data = (item is DBNull) ? String.Empty : item


-1

Tôi sử dụng tiện ích mở rộng để loại bỏ vấn đề này đối với tôi, có thể có hoặc không phải những gì bạn đang theo đuổi.

Nó diễn ra như thế này:

public static class Extensions
{

    public String TrimString(this object item)
    {
        return String.Format("{0}", item).Trim();
    }

}

Ghi chú:

Phần mở rộng này không trả về nullgiá trị! Nếu mục là nullhoặc DBNull.Value , nó sẽ trả về một Chuỗi trống.

Sử dụng:

public string GetCustomerNumber(Guid id)
{
    var obj = 
        DBSqlHelperFactory.ExecuteScalar(
            connectionStringSplendidmyApp, 
            CommandType.StoredProcedure, 
            "GetCustomerNumber", 
            new SqlParameter("@id", id)
        );
    return obj.TrimString();
}

-2

Chuyển đổi nó giống như

string s = System.DBNull.value.ToString();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.