Неможливо передати об’єкт типу "System.DBNull", щоб ввести "System.String"


109

Я отримав вищезгадану помилку в своєму додатку. Ось оригінальний код

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

Я замінив на

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();
    }
}

Чи є кращий шлях до цього?


2
ви дійсно повинні вивчити відповідь @ rein, заощадить вам багато часу в довгостроковій перспективі
roman m

Відповіді:


90

Можна використовувати коротку форму:

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

EDIT: Я не звертав уваги на ExecuteScalar. Це дійсно повертає нуль, якщо поле відсутнє в результаті повернення. Тому використовуйте замість цього:

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

3
Це не буде працювати - "accountNumber" - це не значення бази даних, а звичайний старий звичайний екземпляр об'єкта .NET "object" .NET - вам потрібно перевірити нормальне значення "null". DBNull.Value буде працювати для SqlDataReader або SqlParameter - але не для цього об'єкта тут.
marc_s

Ви маєте рацію, я почав оптимізувати частину перевірки стану, раніше не переглядав рядок. Mea culpa.
Користувач

У вашій публікації є помилка друку, яку я не можу реально редагувати, оскільки для редагування потрібно змінити 6 символів. Може хто - то змінити accountNumber.TosString () в accountNumber.ToString ()
Еріком

@marc_s Залежно від версії db / query, вам потрібно перевірити один із них або навіть обидва. Якщо WHERE не відповідає жодному рядку, ви отримаєте a null, якщо вибраний рядок містить NULLу цьому стовпці, значення повернення - System.DBNull.
Олександр

У першому випадку @Alexander згадує - не відповідає жодному рядку - ви можете покластися на Convert.ToString або будь-який інший метод конвертування, якщо ви добре зі значенням, яке вони повертають при перетворенні з null: порожній рядок для рядків, 0 для числових значень, false для boolean, MinValue для DateTime ... msdn.microsoft.com/en-us/library/vstudio/…
Jaime

199

За допомогою простої загальної функції ви можете зробити це дуже просто. Просто зробіть це:

return ConvertFromDBVal<string>(accountNumber);

за допомогою функції:

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
Так, така функція є єдиним практичним рішенням. Будь-яка вбудована логіка вийде з ладу після того, як ви її скопіювали та вставили тисячу разів. :-)
Крістіан Хейтер

3
це не спрацює, якщо ви спробуєте перетворити 1 на bool (Convert.ToBoolean (1) працює нормально)
roman m

@roman: тож тоді ми хотіли б мати додатковий чек (до перевірки на null), який перевіряє булевий тип ...
IAb Abstract

1
Якщо ви хочете або потрібно використовувати функції Перетворення, це не працює. Існує кілька сценаріїв, де можна віддати перевагу перетворенню на явний склад. @romanm відзначив одну з них. Інший - коли ви працюєте з десятковою комою та піклуєтесь про різні механізми округлення, які використовують Convert.ToInt32 та (int). Колишні круги до найближчого парного значення, в той час як явний виклик просто скорочує значення: stackoverflow.com/questions/1608801/… Якщо можливо, я би усунув NULL з суміші, використовуючи функцію T-SQL ISNULL
Jaime

2
@Jaime Ця функція повинна діяти як неявна передача з типу даних SQL у тип даних C # /. NET. Якщо у вас є потреби в явному складі, не використовуйте цю функцію - виконайте це явно замість цього.
узда

17

ExecuteScalar повернеться

  • null, якщо результату не встановлено
  • інакше перший стовпець першого ряду набору результатів, який може бути DBNull.

Якщо ви знаєте, що перший стовпець набору результатів - це рядок, то для покриття всіх баз потрібно перевірити як null, так і DBNull. Щось на зразок:

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

Вищевказаний код спирається на те, що DBNull.ToString повертає порожню рядок.

Якщо accountNumber був іншим типом (скажімо, цілим числом), вам потрібно буде бути більш чітким:

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

Якщо ви точно знаєте, що у вашому наборі результатів завжди буде принаймні один рядок (наприклад, SELECT COUNT (*) ...), то ви можете пропустити чек на null.

У вашому випадку повідомлення про помилку "Не вдається передати об’єкт типу" System.DBNull "до типу" System.String "" вказує, що перший стовпець вашого набору результатів - це значення DBNUll. Це від передачі до рядка в першому рядку:

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

Коментар Marc_s про те, що вам не потрібно перевіряти наявність DBNull.Value є неправильним.


мій набір результатів не завжди повертає рядок.
Сайф Хан

6

Ви можете використовувати оператор нульового згортання C #

return accountNumber ?? string.Empty;

-1: Це не буде компілюватися: метод повертає рядок, а accountNumber є об'єктом.
Джо

2
повернути Cmd.ExecuteScalar (). ToString () ?? String.Empty;
Чайтанья

повернути Cmd.ExecuteScalar (). ToString () зробив роботу за мене
Таран

3

Є ще один спосіб вирішити це питання. Як щодо модифікації процедури вашого магазину? використовуючи ISNULL (ваше поле, "") sql функцію, ви можете повернути порожній рядок, якщо значення повернення є нульовим.

Тоді у вас є чистий код як оригінальна версія.


3

Це загальний метод, який я використовую для перетворення будь-якого об'єкта, який може бути DBNull.Value:

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

використання:

var result = command.ExecuteScalar();

return result.ConvertDBNull(Convert.ToInt32);

коротше:

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

2

Я думаю, ви можете зробити це так:

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

Якщо accountNumber є null, це означає, що це був DBNull не рядок :)


Або return (accountNumber as string) ?? string.Empty;, якщо акаунт Number як і раніше є object. Якщо ви віддаєте перевагу тримати дзвінок у базі даних на власній лінії.
Брайан

1

String.Concat перетворює значення DBNull та null у порожній рядок.

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

    return String.Concat(accountNumber);

 }

Однак я думаю, що ви втрачаєте щось із зрозумілості коду


1
Що станеться, якщо ти напишеш return "" + accountNumber;?
Зев Шпіц

0

Оскільки я отримав екземпляр, який не є нульовим, і якщо я порівняв з DBNULL, я отримав вилучення Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull', і якщо я спробував змінити порівняння на NULL, він просто не працював (оскільки DBNull є об'єктом), навіть це прийнята відповідь.

Я вирішив просто використовувати ключове слово "є". Тому результат дуже читабельний:

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


-1

Я використовую розширення, щоб усунути цю проблему для мене, яка може бути, а може і не бути такою, яку ви шукаєте.

Виходить так:

public static class Extensions
{

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

}

Примітка:

Це розширення не повертає nullзначення! Якщо елемент є nullабо DBNull.Value , він поверне порожню рядок.

Використання:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.