タイプ「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()

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

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

3
その意志はない作品- 「ACCOUNTNUMBERは」あるではないデータベースの値ではなく、定期的に古い旧来.NET「オブジェクト」インスタンス-あなたは、通常の「ヌル」値に対してチェックする必要があります。DBNull.ValueはSqlDataReaderまたはSqlParameterで機能しますが、ここではこのオブジェクトでは機能しません。
marc_s 2009年

あなたは正しい、私は条件チェックの部分を最適化し始めました、前に行を見ていません。Mea culpa。
ユーザー

あなたの投稿には、6文字の変更が必要なため、実際には編集できないというタイプミスがあります。誰かがaccountNumber.TosString()をaccountNumber.ToString()に変更できますか
Eric

@marc_s db / queryのレイアウトに応じて、それらのいずれかまたは両方に対してチェックする必要があります。WHEREがどの行とも一致しない場合、を取得します。null選択した行がNULLその列にある場合、戻り値はになりSystem.DBNullます。
アレクサンダー

最初のケースでは、@ Alexanderが言及している-どの行とも一致しない-nullからの変換時に返される値が問題ない場合は、Convert.ToStringまたは他のConvertメソッドを使用できます。文字列の場合は空の文字列、数値の場合は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
はい、このような機能が唯一の実用的な解決策です。何千回もインラインロジックをコピーして貼り付けた後は、失敗します。:-)
クリスチャンヘイター

3
1をboolに変換しようとすると、これは機能しません(Convert.ToBoolean(1)は正常に機能します)
roman m

@roman:ブール型をチェックする追加のチェック(nullをチェックする前)が
必要

1
変換関数を使用する必要がある場合は、これは機能しません。明示的なキャストへの変換を好むシナリオがいくつかあります。@romanmはそのうちの1つを指摘しました。もう1つは、小数を処理し、Convert.ToInt32および(int)が使用するさまざまな丸めメカニズムに注意する場合です。最も近い偶数値に元ラウンド、明示的なキャストは、単に値を切り捨てながら:stackoverflow.com/questions/1608801/...可能であれば、私はT-SQL ISNULL関数を使用して、ミックスからNULLを排除する
ハイメ・

2
@Jaimeこの関数は、SQLデータ型からC#/。NETデータ型への暗黙的なキャストのように機能することになっています。明示的なキャストが必要な場合は、この関数を使用しないでください。代わりに明示的にキャストしてください。
2014年

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;

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

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

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

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


結果セットが常に行を返すとは限りません。
Saif Khan、

6

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

return accountNumber ?? string.Empty;

-1:コンパイルされません。メソッドは文字列を返し、accountNumberはオブジェクトです。
ジョー

2
Cmd.ExecuteScalar()。ToString()を返しますか?String.Empty;
Chaitanya、2010年

return Cmd.ExecuteScalar()。ToString()が私のために仕事をしてくれました
Taran

3

この問題を回避する別の方法があります。ストアの手順を変更してみませんか?ISNULL(your field、 "")SQL関数を使用すると、戻り値が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

nullではないインスタンスを取得したため、DBNULLと比較すると例外が発生しOperator '==' cannot be applied to operands of type 'string' and 'system.dbnull'、NULLと比較するように変更しようとしても、(DBNullはオブジェクトであるため)動作しませんでした。

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.