SQL Data Reader - menangani nilai kolom Null

298

Saya menggunakan SQLdatareader untuk membangun POCO dari database. Kode ini berfungsi kecuali ketika menemukan nilai nol dalam database. Misalnya, jika kolom FirstName dalam database berisi nilai nol, pengecualian dilemparkan.

employee.FirstName = sqlreader.GetString(indexFirstName);

Apa cara terbaik untuk menangani nilai nol dalam situasi ini?

DenaliHardtail
sumber

Jawaban:

471

Anda perlu memeriksa IsDBNull:

if(!SqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

Itulah satu-satunya cara andal Anda untuk mendeteksi dan menangani situasi ini.

Saya membungkus hal-hal itu ke dalam metode ekstensi dan cenderung mengembalikan nilai default jika kolomnya memang null:

public static string SafeGetString(this SqlDataReader reader, int colIndex)
{
   if(!reader.IsDBNull(colIndex))
       return reader.GetString(colIndex);
   return string.Empty;
}

Sekarang Anda bisa menyebutnya seperti ini:

employee.FirstName = SqlReader.SafeGetString(indexFirstName);

dan Anda tidak perlu khawatir tentang pengecualian atau nullnilai lagi.

marc_s
sumber
65
Jika seseorang membutuhkan nama kolom daripada indeks, Anda dapat melakukannya: int colIndex = reader.GetOrdinal(fieldname);dan dengan mudah membebani SafeGetStringfungsi @ marc_s .
ilans
Tidak percaya saya ada di sini pada tahun 2019, di VB tidak kurang .......... Terima kasih, sangat membantu
JimmyB
Juga dapat dilakukan seperti ini: int ordinal = reader.GetOrdinal ("col_name"); kamu tidak val = reader.IsDBNull (ordinal)? (uint?) null: reader.GetUInt32 (ordinal);
ed22
Hallo teman-teman! Saya mencoba menyalin dan menempel ke formulir dan kembali dengan kesalahan. "Metode ekstensi harus didefinisikan dalam kelas statis non-generik."
Jansen Malaggay
Jika Anda menggunakan reader.GetOrindal di dalam SafeGetString dan Anda ingin menggunakan SafeGetString di dalam satu lingkaran, bagaimana hal ini dapat dicapai tanpa kinerja menekan?
AspUser7724
223

Anda harus menggunakan asoperator yang dikombinasikan dengan ??operator untuk nilai default. Tipe nilai perlu dibaca sebagai nullable dan diberi default.

employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);

The asOperator menangani casting termasuk cek untuk DBNull.

stevehipwell
sumber
6
Jika seseorang mengubah kolom Usia dari menjadi int ke bigint SQL (c # long), kode Anda akan gagal diam-diam dengan mengembalikan 0. Jawaban dari ZXX adalah IMO yang lebih andal.
Martin Ørding-Thomsen
Saya ingin tahu apakah Anda dapat mengganti default (int) menjadi -1 dan bukan 0
Chris
5
@ Chris - Anda seharusnya bisa mengganti default (int) dengan -1.
stevehipwell
@ Stevo3000 Anda benar! Saya mencobanya dan itu berhasil seperti yang Anda katakan setelah saya memposting, tetapi saya lupa untuk kembali ke halaman ini :)
Chris
5
Ketahuilah bahwa menggunakan "sebagai" di sini dapat menyembunyikan kesalahan indeks. Jika Anda tidak sengaja menggunakan sqlreader[indexAge] as string ?? "", Anda akan selalu mendapatkannya "". Pertimbangkan apakah Anda benar-benar menginginkannya (int?)sqlreader[indexAge] ?? defaultValue, jadi jika SQL Anda berubah, Anda mendapatkan pengecualian dan bukan nilai yang buruk. @ Stevo3000: default (int) adalah 0, bukan -1. @ Chris: Pastikan Anda menggunakan yang Anda inginkan.
me22
30

Untuk string, Anda cukup melemparkan versi objek (diakses menggunakan operator array) dan berakhir dengan string nol untuk nulls:

employee.FirstName = (string)sqlreader[indexFirstName];

atau

employee.FirstName = sqlreader[indexFirstName] as string;

Untuk bilangan bulat, jika Anda menggunakan int nullable, Anda bisa menggunakan GetValueOrDefault ()

employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault();

atau operator penggabungan nol ( ??).

employee.Age = (sqlreader[indexAge] as int?) ?? 0;
Goding Coding
sumber
2
Pemeran eksplisit, seperti dalam contoh pertama Anda, tidak berfungsi. Itu melempar kesalahan yang sama
musefan
@musefan: Apakah bidang Anda sebenarnya adalah string? Jika tidak, Anda akan mendapatkan kesalahan yang berbeda. Ini berfungsi dan ini tidak ada perbedaan nyata antara contoh 1 dan 2 (selain dari sintaks).
Hilang Coding
1
@GoneCoding: Ya itu adalah string yang dapat dibatalkan, dan ini jelas merupakan kasus yang pertama menyebabkan masalah ketika yang kedua bekerja. Saya membayangkan masalah ini disebabkan oleh bagaimana nilai-nilai nol ditangani. Seperti dalam, mereka bukan nullmelainkan objek DBNull. Perbedaan antara dua pernyataan adalah yang pertama akan gagal jika itu bukan string, sedangkan yang kedua hanya akan mengembalikan nol jika bukan string.
musefan
23

IsDbNull(int)biasanya jauh lebih lambat daripada menggunakan metode seperti GetSqlDateTimedan kemudian membandingkannya DBNull.Value. Coba metode ekstensi ini untuk SqlDataReader.

public static T Def<T>(this SqlDataReader r, int ord)
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return default(T);
    return ((INullable)t).IsNull ? default(T) : (T)t;
}

public static T? Val<T>(this SqlDataReader r, int ord) where T:struct
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? (T?)null : (T)t;
}

public static T Ref<T>(this SqlDataReader r, int ord) where T : class
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? null : (T)t;
}

Gunakan mereka seperti ini:

var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);
ZXX
sumber
5
Saya menemukan bahwa operator eksplisit pada tipe System.Data.SqlTypes melempar kesalahan di mana-mana mencoba menggunakan kode ini ...
Tetsujin no Oni
Lihat stackoverflow.com/a/21024873/1508467 untuk penjelasan mengapa ini terkadang gagal (coba gunakan Val <int> untuk membaca kolom int SQL).
Rhys Jones
12

Salah satu cara untuk melakukannya adalah memeriksa db nulls:

employee.FirstName = (sqlreader.IsDBNull(indexFirstName) 
    ? ""
    : sqlreader.GetString(indexFirstName));
Michael Todd
sumber
12

reader.IsDbNull(ColumnIndex) berfungsi seperti banyak jawaban.

Dan saya ingin menyebutkan jika Anda bekerja dengan nama kolom, hanya membandingkan jenis mungkin lebih nyaman.

if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }
PJ3
sumber
Ini juga berfungsi pada versi lama System.Data dan .NET FW
RaSor
11

Saya tidak berpikir ada nilai kolom NULL , ketika baris dikembalikan dalam datareader menggunakan nama kolom.

Jika Anda melakukannya datareader["columnName"].ToString();akan selalu memberi Anda nilai yang dapat berupa string kosong ( String.Emptyjika Anda perlu membandingkan).

Saya akan menggunakan yang berikut ini dan tidak akan terlalu khawatir:

employee.FirstName = sqlreader["columnNameForFirstName"].ToString();
el bayames
sumber
4
Anda dapat melakukan pembaca [FieldName] == DBNull.Value, untuk memeriksa NULL's
Ralph Willgoss
11

Solusi ini tidak terlalu tergantung pada vendor dan berfungsi dengan SQL, OleDB, dan MySQL Reader:

public static string GetStringSafe(this IDataReader reader, int colIndex)
{
    return GetStringSafe(reader, colIndex, string.Empty);
}

public static string GetStringSafe(this IDataReader reader, int colIndex, string defaultValue)
{
    if (!reader.IsDBNull(colIndex))
        return reader.GetString(colIndex);
    else
        return defaultValue;
}

public static string GetStringSafe(this IDataReader reader, string indexName)
{
    return GetStringSafe(reader, reader.GetOrdinal(indexName));
}

public static string GetStringSafe(this IDataReader reader, string indexName, string defaultValue)
{
    return GetStringSafe(reader, reader.GetOrdinal(indexName), defaultValue);
}
Musim panas
sumber
1
Menyalin dan menyesuaikan kode ini langsung ke kelas ekstensi sekarang.
qxotk
8

Apa yang saya cenderung lakukan adalah mengganti nilai nol dalam pernyataan SELECT dengan sesuatu yang sesuai.

SELECT ISNULL(firstname, '') FROM people

Di sini saya mengganti setiap null dengan string kosong. Kode Anda tidak akan melakukan kesalahan dalam hal itu.

alex
sumber
Jika memungkinkan, gunakan ini hindari nulls. Kalau tidak, saya suka jawaban Sonny Boy tentang metode pembantu.
Tidak Ada Pengembalian Uang Tidak Ada Pengembalian
3
Mengapa metode pembantu statis dan terpisah? Bukankah metode ekstensi pada SqlDataReader tampaknya lebih menarik dan lebih intuitif ??
marc_s
7

Anda dapat menulis fungsi Generik untuk memeriksa Null dan memasukkan nilai default ketika NULL. Sebut ini saat membaca Datareader

public T CheckNull<T>(object obj)
        {
            return (obj == DBNull.Value ? default(T) : (T)obj);
        }

Saat membaca penggunaan Datareader

                        while (dr.Read())
                        {
                            tblBPN_InTrRecon Bpn = new tblBPN_InTrRecon();
                            Bpn.BPN_Date = CheckNull<DateTime?>(dr["BPN_Date"]);
                            Bpn.Cust_Backorder_Qty = CheckNull<int?>(dr["Cust_Backorder_Qty"]);
                            Bpn.Cust_Min = CheckNull<int?>(dr["Cust_Min"]);
                         }
Vijai
sumber
6

Periksa sqlreader.IsDBNull(indexFirstName)sebelum Anda mencoba membacanya.

CesarGon
sumber
5

Dengan memengaruhi jawaban getpsyched , saya membuat metode generik yang memeriksa nilai kolom berdasarkan namanya

public static T SafeGet<T>(this System.Data.SqlClient.SqlDataReader reader, string nameOfColumn)
{
  var indexOfColumn = reader.GetOrdinal(nameOfColumn);
  return reader.IsDBNull(indexOfColumn) ? default(T) : reader.GetFieldValue<T>(indexOfColumn);
}

Pemakaian:

var myVariable = SafeGet<string>(reader, "NameOfColumn")
Proyek Mayhem
sumber
Saya tidak tahu siapa Anda, tetapi berkatilah Anda. Fungsi ini menghemat lebih dari tiga jam kerja.
Ramneek Singh
4

bagaimana cara membuat metode pembantu

Untuk String

private static string MyStringConverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return "";

        return o.ToString();
    }

Pemakaian

MyStringConverter(read["indexStringValue"])

Untuk Int

 private static int MyIntonverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return 0;

        return Convert.ToInt32(o);
    }

Pemakaian

MyIntonverter(read["indexIntValue"])

Untuk kencan

private static DateTime? MyDateConverter(object o)
    {
        return (o == DBNull.Value || o == null) ? (DateTime?)null : Convert.ToDateTime(o);
    }

Pemakaian

MyDateConverter(read["indexDateValue"])

Catatan: untuk DateTime mendeklarasikan varialbe sebagai

DateTime? variable;
Ali Usman
sumber
4

Sebagai tambahan untuk jawaban oleh marc_s, Anda dapat menggunakan metode ekstensi yang lebih umum untuk mendapatkan nilai dari SqlDataReader:

public static T SafeGet<T>(this SqlDataReader reader, int col)
    {
        return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
    }
getpsyched
sumber
Saya tidak akan menyebut metode ini "SafeGet" karena jika T adalah struct, itu akan mengkonversi nol ke default non-null untuk T - tidak benar-benar aman. Mungkin "GetValueOrDefault".
Rhys Jones
@RhysJones Mengapa Anda memiliki T sebagai struct dalam kasus ini? Bahkan jika Anda melakukannya saya berpendapat bahwa default non-null dalam struct adalah perilaku yang diharapkan.
getpsyched
@RhysJones Tapi saya setuju bahwa metode ini mungkin tidak aman, perlu menangani pengecualian seperti InvalidCastException dari SqlDataReader.
getpsyched
3

Saya pikir Anda ingin menggunakan:

SqlReader.IsDBNull(indexFirstName)
bytebender
sumber
2

Kami menggunakan serangkaian metode statis untuk menarik semua nilai dari pembaca data kami. Jadi dalam hal ini kami akan meneleponDBUtils.GetString(sqlreader(indexFirstName)) . Manfaat membuat metode statis / bersama adalah Anda tidak perlu melakukan pemeriksaan yang sama berulang-ulang ...

Metode statis akan berisi kode untuk memeriksa nol (lihat jawaban lain di halaman ini).

Sonny Boy
sumber
2

Anda dapat menggunakan operator kondisional:

employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";
Panayot Minkov
sumber
Sama dengan salah satu jawaban di bawah ini, tetapi tertinggal 8 tahun!
beercohol
2

Ada banyak jawaban di sini dengan info berguna (dan beberapa info salah) tersebar, saya ingin menggabungkan semuanya.

Jawaban singkat untuk pertanyaan ini adalah untuk memeriksa DBNull - hampir semua orang setuju pada bagian ini :)

Daripada menggunakan metode pembantu untuk membaca nilai nullable per tipe data SQL metode generik memungkinkan kita untuk mengatasi ini dengan kode yang jauh lebih sedikit. Namun, Anda tidak dapat memiliki metode generik tunggal untuk tipe nilai nullable dan tipe referensi, ini dibahas panjang lebar dalam tipe Nullable sebagai parameter generik yang mungkin? dan batasan tipe generik C # untuk semua yang dapat dibatalkan .

Jadi, sebagai lanjutan dari jawaban dari @ZXX dan @getpsyched kita berakhir dengan ini, 2 metode untuk mendapatkan nilai nullable dan saya telah menambahkan 3 untuk nilai-nilai non-null (melengkapi set berdasarkan metode penamaan).

public static T? GetNullableValueType<T>(this SqlDataReader sqlDataReader, string columnName) where T : struct
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? (T?)null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNullableReferenceType<T>(this SqlDataReader sqlDataReader, string columnName) where T : class
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNonNullValue<T>(this SqlDataReader sqlDataReader, string columnName)
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

Saya biasanya menggunakan nama kolom, ubah ini jika Anda menggunakan indeks kolom. Berdasarkan nama-nama metode ini saya bisa tahu apakah saya mengharapkan data menjadi nullable atau tidak, cukup berguna ketika melihat kode yang ditulis sejak lama.

Kiat;

  • Tidak memiliki kolom yang dapat dibatalkan dalam basis data menghindari masalah ini. Jika Anda memiliki kontrol atas database maka kolom harus non-null secara default dan hanya nullable jika diperlukan.
  • Jangan memberikan nilai basis data dengan operator C # 'sebagai' karena jika gips salah maka diam-diam akan mengembalikan nol.
  • Menggunakan ekspresi nilai default akan mengubah basis data nol menjadi nilai bukan nol untuk jenis nilai seperti int, datetime, bit dll.

Terakhir, saat menguji metode di atas di semua tipe data SQL Server saya menemukan Anda tidak bisa langsung mendapatkan char [] dari SqlDataReader, jika Anda ingin char [] Anda harus mendapatkan string dan menggunakan ToCharArray ().

Rhys Jones
sumber
1

Saya menggunakan kode yang tercantum di bawah ini untuk menangani sel-sel nol dalam lembar Excel yang dibaca ke datatable.

if (!reader.IsDBNull(2))
{
   row["Oracle"] = (string)reader[2];
}
Tequila
sumber
1
private static void Render(IList<ListData> list, IDataReader reader)
        {
            while (reader.Read())
            {

                listData.DownUrl = (reader.GetSchemaTable().Columns["DownUrl"] != null) ? Convert.ToString(reader["DownUrl"]) : null;
                //没有这一列时,让其等于null
                list.Add(listData);
            }
            reader.Close();
        }
xux
sumber
1

dan / atau menggunakan operator ternary dengan penugasan:

employee.FirstName = rdr.IsDBNull(indexFirstName))? 
                     String.Empty: rdr.GetString(indexFirstName);

ganti nilai default (bila nol) yang sesuai untuk setiap jenis properti ...

Charles Bretana
sumber
1

Metode ini tergantung pada indexFirstName yang seharusnya menjadi ordinal kolom berbasis nol.

if(!sqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

Jika Anda tidak tahu indeks kolom tetapi tidak ingin memeriksa nama, Anda dapat menggunakan metode ekstensi ini sebagai gantinya:

public static class DataRecordExtensions
{
    public static bool HasColumn(this IDataRecord dr, string columnName)
    {
        for (int i=0; i < dr.FieldCount; i++)
        {
            if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
                return true;
        }
        return false;
    }
}

Dan gunakan metode seperti ini:

if(sqlReader.HasColumn("FirstName"))
{
  employee.FirstName = sqlreader["FirstName"];
}
Ogglas
sumber
1

Pertanyaan lama tapi mungkin seseorang masih butuh jawaban

sebenarnya saya mengatasi masalah ini seperti itu

Untuk int:

public static object GatDataInt(string Query, string Column)
    {
        SqlConnection DBConn = new SqlConnection(ConnectionString);
        if (DBConn.State == ConnectionState.Closed)
            DBConn.Open();
        SqlCommand CMD = new SqlCommand(Query, DBConn);
        SqlDataReader RDR = CMD.ExecuteReader();
        if (RDR.Read())
        {
            var Result = RDR[Column];
            RDR.Close();
            DBConn.Close();
            return Result;
        }
        return 0;
    }

sama untuk string, kembalikan saja "" alih-alih 0 karena "" adalah string kosong

jadi kamu bisa menggunakannya seperti

int TotalPoints = GatDataInt(QueryToGetTotalPoints, TotalPointColumn) as int?;

dan

string Email = GatDatastring(QueryToGetEmail, EmailColumn) as string;

sangat fleksibel sehingga Anda dapat memasukkan kueri apa pun untuk membaca kolom apa pun dan itu tidak akan pernah kembali dengan kesalahan

Ahmed Kamal
sumber
0

Ini adalah kelas pembantu yang bisa digunakan orang lain jika mereka perlu berdasarkan jawaban @marc_s:

public static class SQLDataReaderExtensions
    {
        public static int SafeGetInt(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? 0 : dataReader.GetInt32(fieldIndex);
        }

        public static int? SafeGetNullableInt(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.GetValue(fieldIndex) as int?;
        }

        public static string SafeGetString(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? string.Empty : dataReader.GetString(fieldIndex);
        }

        public static DateTime? SafeGetNullableDateTime(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.GetValue(fieldIndex) as DateTime?;
        }

        public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName)
        {
            return SafeGetBoolean(dataReader, fieldName, false);
        }

        public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName, bool defaultValue)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? defaultValue : dataReader.GetBoolean(fieldIndex);
        }
    }
Raghav
sumber
0

Konversi menangani DbNull secara masuk akal.

employee.FirstName = Convert.ToString(sqlreader.GetValue(indexFirstName));
Frank Hagenson
sumber
Perhatikan bahwa DBNull dikonversi ke string kosong, bukan nilai nol.
Rhys Jones
-2

Anda bisa memeriksa ini juga

if(null !=x && x.HasRows)
{ ....}
scooby
sumber
-1 Ini bukan intinya: kami menangani kasus nilai kolom nol, bukan nol atau kosongSqlDataReader
kebiruan