Невозможно привести объект типа 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, это сэкономит вам много времени в долгосрочной перспективе
Роман М.

Ответы:


90

Можно использовать более короткую форму:

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

РЕДАКТИРОВАТЬ: Не обращал внимания на ExecuteScalar. Он действительно возвращает null, если поле отсутствует в возвращаемом результате. Поэтому используйте вместо этого:

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

3
Это не сработает - «accountNumber» - это не значение базы данных, а обычный старый экземпляр «объекта» Plain Old .NET - вам нужно проверить нормальное «нулевое» значение. DBNull.Value будет работать для SqlDataReader или SqlParameter, но не для этого объекта здесь.
marc_s

Вы правы, я начал оптимизировать часть проверки условий, раньше на строку не смотрел. Моя вина.
Пользователь

В вашем сообщении есть опечатка, которую я не могу отредактировать, потому что для редактирования нужно изменить 6 символов. Может ли кто-нибудь изменить accountNumber.TosString () на accountNumber.ToString ()
Эрик

@marc_s В зависимости от макета базы данных / запроса вам необходимо проверить любой из них или даже оба. Если WHERE не соответствует ни одной строке, вы получите null, если выбранная строка имеет NULLв этом столбце, возвращаемое значение System.DBNull.
Александр

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

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
Да, такая функция - единственное практическое решение. Любая встроенная логика выйдет из строя после того, как вы скопируете и вставите ее тысячу раз. :-)
Christian Hayter

3
это не сработает, если вы попытаетесь преобразовать 1 в bool (Convert.ToBoolean (1) отлично работает)
roman m

@roman: тогда мы бы хотели провести дополнительную проверку (перед проверкой на null), которая проверяет логический тип ...
IAbstract

1
Если вы хотите или вам нужно использовать функции преобразования, это не сработает. Есть несколько сценариев, в которых вы можете предпочесть преобразование в явное приведение. @romanm заметил одну из них. Другой - когда вы работаете с десятичными знаками и заботитесь о различных механизмах округления, которые используют Convert.ToInt32 и (int). Первый округляет до ближайшего четного значения, тогда как явное приведение просто обрезает значение: stackoverflow.com/questions/1608801/… Если возможно, я бы исключил NULL из смеси, используя функцию T-SQL ISNULL
Хайме

2
@Jaime Эта функция должна действовать как неявное приведение типа данных SQL к типу данных C # /. NET. Если вам нужно явное приведение, не используйте эту функцию - сделайте это явно.
Rein

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, неверен.


мой набор результатов не всегда возвращает строку.
Саиф Хан,

7

Вы можете использовать оператор объединения NULL в C #

return accountNumber ?? string.Empty;

-1: Это не скомпилируется: метод возвращает строку, а accountNumber является объектом.
Джо

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

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

3

Есть еще один способ обойти эту проблему. Как насчет изменения процедуры вашего магазина? используя функцию sql ISNULL (ваше поле, ""), вы можете вернуть пустую строку, если возвращаемое значение равно null.

Тогда у вас будет чистый код в виде оригинальной версии.


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;, если accountNumber по-прежнему является 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;?
Zev Spitz

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.