Pertanyaan ini muncul sesekali, tetapi saya belum melihat jawaban yang memuaskan.
Pola khas adalah (baris adalah DataRow ):
if (row["value"] != DBNull.Value)
{
someObject.Member = row["value"];
}
Pertanyaan pertama saya adalah mana yang lebih efisien (saya telah membalik kondisinya):
row["value"] == DBNull.Value; // Or
row["value"] is DBNull; // Or
row["value"].GetType() == typeof(DBNull) // Or... any suggestions?
Ini menunjukkan bahwa .GetType () harus lebih cepat, tetapi mungkin kompiler tahu beberapa trik yang tidak saya lakukan?
Pertanyaan kedua, apakah layak untuk men-cache nilai baris ["value"] atau apakah kompiler mengoptimalkan pengindeks?
Sebagai contoh:
object valueHolder;
if (DBNull.Value == (valueHolder = row["value"])) {}
Catatan:
- baris ["value"] ada.
- Saya tidak tahu indeks kolom dari kolom (karenanya pencarian nama kolom).
- Saya bertanya secara khusus tentang memeriksa DBNull dan kemudian tugas (bukan tentang optimasi prematur, dll.).
Saya membandingkan beberapa skenario (waktu dalam detik, 10.000.000 uji coba):
row["value"] == DBNull.Value: 00:00:01.5478995
row["value"] is DBNull: 00:00:01.6306578
row["value"].GetType() == typeof(DBNull): 00:00:02.0138757
Object.ReferenceEquals memiliki kinerja yang sama dengan "=="
Hasil paling menarik? Jika Anda tidak cocok dengan nama kolom berdasarkan huruf (misalnya, "Nilai" dan bukan "nilai", dibutuhkan kira-kira sepuluh kali lebih lama (untuk string):
row["Value"] == DBNull.Value: 00:00:12.2792374
Moral dari cerita tersebut adalah bahwa jika Anda tidak dapat mencari kolom berdasarkan indeksnya, maka pastikan bahwa nama kolom yang Anda masukkan ke pengindeks sama persis dengan nama DataColumn.
Caching nilainya juga tampaknya hampir dua kali lebih cepat:
No Caching: 00:00:03.0996622
With Caching: 00:00:01.5659920
Jadi metode yang paling efisien tampaknya menjadi:
object temp;
string variable;
if (DBNull.Value != (temp = row["value"]))
{
variable = temp.ToString();
}
IDataRecord
ekstensi.Jawaban:
Saya pasti melewatkan sesuatu. Tidak memeriksa
DBNull
persis apa yang dilakukanDataRow.IsNull
metode ini?Saya telah menggunakan dua metode ekstensi berikut:
Pemakaian:
Jika Anda tidak ingin
Nullable<T>
mengembalikan nilaiGetValue<T>
, Anda dapat dengan mudah mengembalikandefault(T)
atau opsi lain.Pada catatan yang tidak terkait, inilah alternatif VB.NET untuk saran Stevo3000:
sumber
row.IsNull(columnName)
Anda sudah membacanya sekali dan membacanya lagi. Tidak mengatakan itu akan membuat perbedaan, tetapi secara teoritis itu bisa kurang efisien ..System.Data.DataSetExtensions.DataRowExtensions.Field<T>(this System.Data.DataRow, string)
pada dasarnya melakukan hal yang sama dengan metode pertama?Anda harus menggunakan metode ini:
Mempertimbangkan itu sudah ada di dalam Kerangka, saya berharap ini menjadi yang paling efisien.
Saya akan menyarankan sesuatu di sepanjang baris:
Dan ya, kompiler harus menyimpannya untuk Anda.
sumber
Kompiler tidak akan mengoptimalkan pengindeks jauh (yaitu jika Anda menggunakan baris ["nilai"] dua kali), jadi ya itu sedikit lebih cepat untuk dilakukan:
dan kemudian gunakan nilai dua kali; menggunakan .GetType () berisiko menimbulkan masalah jika itu nol ...
DBNull.Value
sebenarnya adalah singleton, jadi untuk menambahkan opsi ke-4 - Anda mungkin bisa menggunakan ReferenceEquals - tetapi dalam kenyataannya, saya pikir Anda terlalu khawatir di sini ... Saya tidak berpikir kecepatan berbeda antara "is", "== "dll akan menjadi penyebab masalah kinerja yang Anda lihat. Profil seluruh kode Anda dan fokus pada sesuatu yang penting ... tidak akan seperti ini.sumber
Saya akan menggunakan kode berikut dalam C # ( VB.NET tidak sesederhana).
Kode memberikan nilai jika bukan null / DBNull, jika tidak, menugaskan default yang dapat diatur ke nilai LHS yang memungkinkan kompiler mengabaikan penetapan.
sumber
oSomeObject.IntMember = If(TryCast(oRow("Value), Integer?), iDefault)
.TryCast
tidak menyediakan fungsionalitas nyaman yang sama denganas
operator C # untukNullable(Of T)
tipe. Cara terdekat yang bisa saya pikirkan untuk meniru ini adalah dengan menulis fungsi Anda sendiri, seperti yang sekarang saya sarankan dalam jawaban saya.Saya merasa hanya sedikit pendekatan di sini yang tidak mengkhawatirkan prospek OP yang paling khawatir (Marc Gravell, Stevo3000, Richard Szalay, Neil, Darren Koppand) dan sebagian besar tidak perlu rumit. Menyadari sepenuhnya ini adalah optimasi mikro yang tidak berguna, saya katakan Anda pada dasarnya harus menggunakan ini:
1) Jangan membaca nilai dari DataReader / DataRow dua kali - jadi temboloklah sebelum cek nol dan gips / konversi atau bahkan lebih baik langsung meneruskan
record[X]
objek Anda ke metode ekstensi khusus dengan tanda tangan yang sesuai.2) Untuk mematuhi hal di atas, jangan gunakan
IsDBNull
fungsi bawaan pada DataReader / DataRow Anda karena itu memanggilrecord[X]
internal, jadi Anda akan melakukannya dua kali.3) Jenis perbandingan akan selalu lebih lambat daripada perbandingan nilai sebagai aturan umum. Lakukan
record[X] == DBNull.Value
lebih baik.4) casting langsung akan lebih cepat daripada memanggil
Convert
kelas untuk mengkonversi, meskipun saya khawatir yang terakhir akan lebih sedikit goyah.5) Terakhir, mengakses catatan dengan indeks daripada nama kolom akan lebih cepat lagi.
Saya merasa berjalan dengan pendekatan Szalay, Neil dan Darren Koppand akan lebih baik. Saya terutama menyukai pendekatan metode ekstensi Darren Koppand yang menerima
IDataRecord
(meskipun saya ingin mempersempitnya lebih jauh keIDataReader
) dan indeks / nama kolom.Berhati-hatilah untuk menyebutnya:
dan tidak
jika Anda perlu membedakan antara
0
danDBNull
. Misalnya, jika Anda memiliki nilai nol di bidang enum, jika tidak makadefault(MyEnum)
risiko nilai enum pertama akan dikembalikan. Panggilan yang lebih baikrecord.GetColumnValue<MyEnum?>("Field")
.Karena Anda membaca dari a
DataRow
, saya akan membuat metode ekstensi untuk keduanyaDataRow
danIDataReader
dengan MENGURANGI kode umum.Jadi sekarang menyebutnya seperti:
Saya percaya ini adalah bagaimana seharusnya dalam kerangka (bukan metode
record.GetInt32
,record.GetString
dll) di tempat pertama - tidak ada pengecualian run-time dan memberi kita fleksibilitas untuk menangani nilai nol.Dari pengalaman saya, saya kurang beruntung dengan satu metode umum untuk membaca dari database. Saya selalu harus menangani kustom berbagai jenis, jadi saya harus menulis sendiri saya
GetInt
,GetEnum
,GetGuid
, dll metode dalam jangka panjang. Bagaimana jika Anda ingin memotong spasi putih saat membaca string dari db secara default, atau memperlakukanDBNull
sebagai string kosong? Atau jika desimal Anda harus dipotong dari semua nol yang tertinggal. Saya memiliki banyak masalah denganGuid
tipe di mana driver konektor yang berbeda berperilaku berbeda juga ketika database yang mendasarinya dapat menyimpannya sebagai string atau biner. Saya memiliki kelebihan seperti ini:Dengan pendekatan Stevo3000, saya merasa panggilannya agak jelek dan membosankan, dan akan lebih sulit untuk membuat fungsi generik darinya.
sumber
Ada kasus yang merepotkan di mana objek bisa menjadi string. Kode metode ekstensi di bawah ini menangani semua kasus. Begini cara Anda menggunakannya:
sumber
Saya pribadi menyukai sintaks ini, yang menggunakan metode IsDbNull eksplisit diekspos oleh
IDataRecord
, dan cache indeks kolom untuk menghindari pencarian string duplikat.Diperluas agar mudah dibaca, seperti:
Ditulis ulang agar sesuai pada satu baris untuk kekompakan dalam kode DAL - perhatikan bahwa dalam contoh ini kami menetapkan
int bar = -1
jikarow["Bar"]
nol.Tugas sebaris dapat membingungkan jika Anda tidak tahu itu ada di sana, tetapi itu membuat seluruh operasi pada satu baris, yang saya pikir meningkatkan keterbacaan ketika Anda mempopulasikan properti dari beberapa kolom dalam satu blok kode.
sumber
Bukannya saya sudah melakukan ini, tetapi Anda bisa mengatasi panggilan indekser ganda dan masih menjaga kode Anda bersih dengan menggunakan metode statis / ekstensi.
Yaitu.
Kemudian:
Juga memiliki manfaat menjaga logika pemeriksaan nol di satu tempat. Kelemahannya, tentu saja, ini adalah panggilan metode tambahan.
Hanya pemikiran saja.
sumber
Saya berusaha menghindari cek ini sebanyak mungkin.
Jelas tidak perlu dilakukan untuk kolom yang tidak bisa menampung
null
.Jika Anda menyimpan dalam tipe nilai Nullable (
int?
, dll.), Anda bisa mengonversi menggunakanas int?
.Jika Anda tidak perlu membedakan antara
string.Empty
dannull
, Anda cukup menelepon.ToString()
, karena DBNull akan kembalistring.Empty
.sumber
Saya selalu menggunakan:
Menemukannya singkat dan komprehensif.
sumber
Ini adalah bagaimana saya menangani membaca dari DataRows
Contoh penggunaan:
Alat Peraga untuk Monster Mendapat .Net untuk kode ChageTypeTo Saya.
sumber
Saya telah melakukan sesuatu yang mirip dengan metode ekstensi. Ini kode saya:
Untuk menggunakannya, Anda akan melakukan sesuatu seperti
sumber
jika dalam DataRow baris ["fieldname"] isDbNull ganti dengan 0 jika tidak, dapatkan nilai desimal:
sumber
gunakan seperti ini
sumber
Saya memiliki IsDBNull dalam program yang membaca banyak data dari database. Dengan IsDBNull memuat data dalam waktu sekitar 20 detik. Tanpa IsDBNull, sekitar 1 detik.
Jadi saya pikir lebih baik menggunakan:
sumber