web-dev-qa-db-ja.com

タイプ 'System.DBNull'のオブジェクトをタイプ 'System.String`にキャストできません

アプリで上記のエラーが発生しました。ここに元のコードがあります

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

これを回避するより良い方法はありますか?

93
Saif Khan

より短い形式を使用できます。

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

編集:ExecuteScalarに注意を払っていません。返される結果にフィールドが存在しない場合、実際にはnullを返します。代わりに使用してください:

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

単純な汎用関数を使用すると、これを非常に簡単に行うことができます。これを行うだけです:

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;
    }
}
183
rein

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;

結果セットに常に少なくとも1つの行があることが確実な場合(例:SELECT COUNT(*)...)、nullのチェックをスキップできます。

あなたの場合、「タイプ 'System.DBNull'のオブジェクトをタイプ 'System.String`にキャストできません」というエラーメッセージは、結果セットの最初の列がDBNUll値であることを示しています。これは、最初の行の文字列へのキャストからです。

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

DBNull.Valueをチェックする必要がないというMarc_sのコメントは間違っています。

15
Joe

C#のnull合体演算子を使用できます

return accountNumber ?? string.Empty;
6
Nathan Koop

これは、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);
3
Heras

この問題を回避する別の方法があります。店舗の手続きを変更してはどうですか? ISNULL(your field、 "")sql functionを使用すると、戻り値がnullの場合に空の文字列を返すことができます。

次に、元のバージョンとしてクリーンなコードがあります。

3
Russel Yang

私はあなたがこのようにできると思う:

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

AccountNumberがnullの場合、文字列ではなくDBNullであったことを意味します:)

2
ppiotrowicz

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
Andrea Parodi

Nullではないインスタンスを取得し、DBNULLと比較するとOperator '==' cannot be applied to operands of type 'string' and 'system.dbnull' exeptionを取得し、NULLと比較するように変更しようとしても、単に動作しませんでした(DBNullはオブジェクトであるため)それが受け入れられた答えです。

「is」キーワードを使用することにしました。したがって、結果は非常に読みやすくなります。

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

0
Remy