Tak dapat mentransmisikan objek berjenis 'System.DBNull' untuk mengetik 'System.String`

109

Saya mendapat kesalahan di atas di aplikasi saya. Ini kode aslinya

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

Saya ganti dengan

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

Apakah ada cara yang lebih baik untuk mengatasi ini?

Saif Khan
sumber
2
Anda harus benar-benar melihat jawaban @ rein, akan menghemat banyak waktu dalam jangka panjang
roman m

Jawaban:

90

Bentuk yang lebih pendek dapat digunakan:

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

EDIT: Belum memperhatikan ExecuteScalar. Itu benar-benar mengembalikan nol jika bidang tidak ada di hasil pengembalian. Jadi gunakan saja:

return (accountNumber == null) ? string.Empty : accountNumber.ToString() 
Pengguna
sumber
3
Itu tidak akan berhasil - "accountNumber" bukanlah nilai database tetapi instance "objek" .NET Lama Biasa biasa - Anda perlu memeriksa nilai "null" normal. DBNull.Value akan berfungsi untuk SqlDataReader atau SqlParameter - tetapi tidak untuk objek ini di sini.
marc_s
Anda benar, saya mulai mengoptimalkan bagian pemeriksaan kondisi, belum melihat garis sebelumnya. MEA Culpa.
Pengguna
Ada kesalahan ketik di postingan Anda yang tidak dapat saya edit karena pengeditan memerlukan 6 karakter untuk diubah. Dapatkah seseorang mengubah accountNumber.TosString () menjadi accountNumber.ToString ()
Eric
@marc_s Tergantung pada db / query layout, Anda perlu memeriksa salah satunya atau bahkan keduanya. Jika WHERE tidak cocok dengan baris mana pun, Anda akan mendapatkan a null, jika baris yang dipilih ada NULLdi kolom itu, nilai yang dikembalikan adalah System.DBNull.
Alexander
Dalam kasus pertama @Alexander menyebutkan -tidak cocok dengan baris apa pun- Anda dapat mengandalkan Convert.ToString atau metode Convert lainnya jika Anda baik-baik saja dengan nilai yang mereka kembalikan saat mengonversi dari null: string kosong untuk string, 0 untuk nilai numerik, salah untuk boolean, MinValue untuk DateTime ... msdn.microsoft.com/en-us/library/vstudio/…
Jaime
199

Dengan fungsi umum sederhana, Anda dapat membuat ini sangat mudah. Lakukan saja ini:

return ConvertFromDBVal<string>(accountNumber);

menggunakan fungsi:

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;
    }
}
kendali
sumber
1
Ya, fungsi seperti ini adalah satu-satunya solusi praktis. Segala jenis logika sebaris akan gagal setelah Anda menyalin dan menempelkannya ribuan kali. :-)
Christian Hayter
3
ini tidak akan berhasil jika Anda mencoba mengubah 1 ke bool (Convert.ToBoolean (1) berfungsi dengan baik)
roman m
@roman: jadi kami ingin memiliki pemeriksaan tambahan (sebelum memeriksa null) yang memeriksa jenis boolean ...
IAbstract
1
Jika Anda ingin atau perlu menggunakan fungsi Convert, maka ini tidak berfungsi. Ada beberapa skenario di mana Anda mungkin lebih suka mengonversi ke pemeran eksplisit. @romanm mencatat salah satunya. Satu lagi adalah ketika Anda bekerja dengan desimal dan peduli dengan mekanisme pembulatan berbeda yang digunakan Convert.ToInt32 dan (int). Putaran sebelumnya ke nilai genap terdekat, sementara cast eksplisit hanya memotong nilai: stackoverflow.com/questions/1608801/… Jika memungkinkan, saya akan menghilangkan NULL dari campuran, menggunakan fungsi T-SQL ISNULL
Jaime
2
@Jaime Fungsi ini seharusnya bertindak seperti transmisi implisit dari tipe data SQL ke tipe data C # /. NET. Jika Anda membutuhkan cast eksplisit, jangan gunakan fungsi ini - lakukan secara eksplisit.
kendalikan
17

ExecuteScalar akan kembali

  • null jika tidak ada hasil yang ditetapkan
  • jika tidak kolom pertama dari baris pertama dari kumpulan hasil, yang mungkin DBNull.

Jika Anda tahu bahwa kolom pertama dari kumpulan hasil adalah string, maka untuk mencakup semua basis Anda perlu memeriksa null dan DBNull. Sesuatu seperti:

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

Kode di atas bergantung pada fakta bahwa DBNull.ToString mengembalikan string kosong.

Jika accountNumber adalah tipe lain (katakanlah integer), maka Anda harus lebih eksplisit:

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

Jika Anda tahu pasti bahwa kumpulan hasil Anda akan selalu memiliki setidaknya satu baris (misalnya, PILIH JUMLAH (*) ...), maka Anda dapat melewati pemeriksaan null.

Dalam kasus Anda, pesan kesalahan "Tidak dapat mentransmisikan objek jenis 'System.DBNull' untuk mengetik 'System.String`" menunjukkan bahwa kolom pertama dari kumpulan hasil Anda adalah nilai DBNUll. Ini dari cast ke string di baris pertama:

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

Marc_s berkomentar bahwa Anda tidak perlu memeriksa DBNull.Value salah.

Joe
sumber
hasil saya tidak akan selalu menghasilkan satu baris.
Saif Khan
6

Anda dapat menggunakan operator penggabungan null C #

return accountNumber ?? string.Empty;
Nathan Koop
sumber
-1: Itu tidak dapat dikompilasi: metode ini mengembalikan string dan nomor akun adalah objek.
Joe
2
return Cmd.ExecuteScalar (). ToString () ?? String.Empty;
Chaitanya
return Cmd.ExecuteScalar (). ToString () melakukan pekerjaan itu untuk saya
Taran
3

Ada cara lain untuk mengatasi masalah ini. Bagaimana cara mengubah prosedur toko Anda? dengan menggunakan fungsi sql ISNULL (bidang Anda, ""), Anda dapat mengembalikan string kosong jika nilai yang dikembalikan null.

Kemudian Anda memiliki kode bersih sebagai versi aslinya.

Russel Yang
sumber
3

Ini adalah metode umum yang saya gunakan untuk mengonversi objek apa pun yang mungkin menjadi DBNull. Nilai:

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

pemakaian:

var result = command.ExecuteScalar();

return result.ConvertDBNull(Convert.ToInt32);

singkat:

return command
    .ExecuteScalar()
    .ConvertDBNull(Convert.ToInt32);
Heras
sumber
2

Saya kira Anda bisa melakukannya seperti ini:

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

Jika accountNumber adalah null itu berarti DBNull bukan string :)

ppiotrowicz.dll
sumber
Atau return (accountNumber as string) ?? string.Empty;, dengan accountNumber masih menjadi object. Jika Anda lebih suka menyimpan panggilan database Anda di jalurnya sendiri.
Brian
1

String.Concat mengubah nilai DBNull dan null menjadi string kosong.

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

    return String.Concat(accountNumber);

 }

Namun, saya pikir Anda kehilangan sesuatu karena kode dapat dimengerti

Andrea Parodi
sumber
1
Apa yang terjadi jika Anda menulis return "" + accountNumber;?
Zev Spitz
0

Karena saya mendapat contoh yang tidak null dan jika saya dibandingkan dengan DBNULL saya mendapat pengecualian Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull', dan jika saya mencoba mengubah untuk membandingkan dengan NULL, itu tidak berhasil (karena DBNull adalah sebuah objek) bahkan itu adalah jawaban yang diterima.

Saya memutuskan untuk hanya menggunakan kata kunci 'adalah'. Jadi hasilnya sangat mudah dibaca:

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

Remy
sumber
-1

Saya menggunakan ekstensi untuk menghilangkan masalah ini untuk saya, yang mungkin Anda cari atau tidak.

Ini berjalan seperti ini:

public static class Extensions
{

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

}

catatan:

Ekstensi ini tidak mengembalikan nullnilai! Jika itemnya nullatau DBNull.Value , ia akan mengembalikan String kosong.

Pemakaian:

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

Ubah seperti

string s = System.DBNull.value.ToString();
Sudhakar Rao
sumber