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;
}
}
Menggunakan Exception
s untuk logika kontrol seperti dalam beberapa jawaban lain dianggap praktik buruk dan memiliki biaya kinerja. Itu juga mengirimkan positif palsu ke profiler dari # pengecualian yang dilemparkan dan tuhan membantu siapa pun mengatur debugger mereka untuk memecahkan pengecualian yang dilemparkan.
GetSchemaTable () juga merupakan saran lain dalam banyak jawaban. Ini tidak akan menjadi cara yang dipilih untuk memeriksa keberadaan bidang karena tidak diterapkan di semua versi (abstrak dan melempar NotSupportedException di beberapa versi dotnetcore). GetSchemaTable juga kinerja yang berlebihan karena ini adalah fungsi tugas yang cukup berat jika Anda memeriksa sumbernya .
Melewati bidang dapat memiliki hit kinerja kecil jika Anda sering menggunakannya dan Anda mungkin ingin mempertimbangkan untuk menyimpan hasilnya.
Jauh lebih baik menggunakan fungsi boolean ini:Satu panggilan - tanpa pengecualian. Mungkin ada pengecualian internal, tapi saya rasa tidak.CATATAN: Dalam komentar di bawah ini, kami menemukan ini ... kode yang benar adalah ini:
sumber
Saya pikir taruhan terbaik Anda adalah memanggil GetOrdinal ("columnName") di DataReader Anda di muka, dan tangkap IndexOutOfRangeException jika kolom tidak ada.
Bahkan, mari kita buat metode ekstensi:
Edit
Ok, postingan ini mulai mendapatkan beberapa down-vote belakangan ini, dan saya tidak bisa menghapusnya karena ini adalah jawaban yang diterima, jadi saya akan memperbaruinya dan (saya harap) mencoba untuk membenarkan penggunaan penanganan pengecualian sebagai aliran kontrol.
Cara lain untuk mencapai ini, seperti yang diposting oleh Chad Grant , adalah untuk mengulang setiap bidang dalam DataReader dan melakukan perbandingan case-insensitive untuk nama bidang yang Anda cari. Ini akan bekerja dengan sangat baik, dan sejujurnya mungkin akan tampil lebih baik daripada metode saya di atas. Tentu saja saya tidak akan pernah menggunakan metode di atas dalam satu loop di mana performace menjadi masalah.
Saya dapat memikirkan satu situasi di mana metode coba / GetOrdinal / catch akan bekerja di mana loop tidak. Namun, ini adalah situasi yang sepenuhnya hipotetis saat ini sehingga ini merupakan pembenaran yang sangat lemah. Apapun, bersabarlah dan lihat apa yang Anda pikirkan.
Bayangkan sebuah database yang memungkinkan Anda untuk "alias" kolom dalam sebuah tabel. Bayangkan saya bisa mendefinisikan tabel dengan kolom yang disebut "EmployeeName" tetapi juga memberikannya alias "EmpName", dan melakukan pilih untuk salah satu nama akan mengembalikan data di kolom itu. Dengan saya sejauh ini?
Sekarang bayangkan ada penyedia ADO.NET untuk database itu, dan mereka telah membuat kode implementasi IDataReader untuknya yang memperhitungkan alias kolom.
Sekarang,
dr.GetName(i)
(seperti yang digunakan dalam jawaban Chad) hanya dapat mengembalikan satu string, sehingga harus mengembalikan hanya satu dari "alias" pada kolom. Namun,GetOrdinal("EmpName")
dapat menggunakan implementasi internal bidang penyedia ini untuk memeriksa alias setiap kolom untuk nama yang Anda cari.Dalam situasi "alias kolom" hipotetis ini, metode coba / GetOrdinal / catch akan menjadi satu-satunya cara untuk memastikan bahwa Anda memeriksa setiap variasi nama kolom di resultset.
Tipis? Tentu. Tapi patut dipikirkan. Jujur saya lebih suka metode HasColumn "resmi" pada IDataRecord.
sumber
Dalam satu baris, gunakan ini setelah pengambilan DataReader Anda:
Kemudian,
Edit
Satu liner yang jauh lebih efisien yang tidak perlu memuat skema:
sumber
Berikut adalah contoh kerja untuk ide Jasmin:
sumber
ini bekerja untuk saya:
sumber
Berikut ini sederhana dan berfungsi untuk saya:
sumber
Jika Anda membaca pertanyaan, Michael bertanya tentang DataReader, bukan orang-orang DataRecord. Dapatkan benda Anda dengan benar.
Menggunakan
r.GetSchemaTable().Columns.Contains(field)
pada DataRecord berfungsi, tetapi mengembalikan kolom BS (lihat tangkapan layar di bawah.)Untuk melihat apakah ada kolom data DAN berisi data dalam DataReader, gunakan ekstensi berikut:
Pemakaian:
Memanggil
r.GetSchemaTable().Columns
DataReader mengembalikan kolom BS:sumber
IDataReader
mengimplementasikanIDataRecord
. Mereka adalah antarmuka yang berbeda dari objek yang sama - sama sepertiICollection<T>
danIEnumerable<T>
merupakan antarmuka yang berbeda dariList<T>
.IDataReader
memungkinkan maju ke catatan berikutnya, sementaraIDataRecord
memungkinkan membaca dari catatan saat ini. Metode yang digunakan dalam jawaban ini semua berasal dariIDataRecord
antarmuka. Lihat stackoverflow.com/a/1357743/221708 untuk penjelasan mengapa mendeklarasikan parameter sebagaiIDataRecord
lebih disukai.r.GetSchemaTable().Columns
jawaban yang benar-benar salah untuk pertanyaan ini.Saya menulis untuk pengguna Visual Basic:
Saya pikir ini lebih kuat dan penggunaannya adalah:
sumber
Berikut ini adalah versi linq liner dari jawaban yang diterima:
sumber
Di sini solusi dari Jasmine dalam satu baris ... (satu lagi, sederhana!):
sumber
sumber
TLDR:
Banyak jawaban dengan klaim tentang kinerja dan praktik buruk, jadi saya jelaskan di sini.
Rute pengecualian lebih cepat untuk jumlah kolom yang dikembalikan lebih tinggi, rute loop lebih cepat untuk jumlah kolom yang lebih rendah, dan titik crossover sekitar 11 kolom. Gulir ke bawah untuk melihat grafik dan kode uji.
Jawaban lengkap:
Kode untuk beberapa jawaban teratas berfungsi, tetapi ada debat mendasar di sini untuk jawaban "lebih baik" berdasarkan penerimaan penanganan pengecualian dalam logika dan terkait kinerjanya.
Untuk menghapusnya, saya tidak percaya ada banyak panduan tentang pengecualian PENCOCOKAN. Microsoft memang memiliki beberapa panduan tentang pengecualian. Di sana mereka menyatakan:
Catatan pertama adalah keringanan hukuman "jika mungkin". Lebih penting lagi, deskripsi memberikan konteks ini:
Artinya adalah jika Anda menulis API yang mungkin dikonsumsi oleh orang lain, beri mereka kemampuan untuk menavigasi pengecualian tanpa mencoba / menangkap. Misalnya, berikan TryParse dengan metode Parse pelempar pengecualian Anda. Namun tidak ada yang mengatakan bahwa Anda tidak harus menangkap pengecualian.
Lebih jauh, seperti yang ditunjukkan oleh pengguna lain, tangkapan selalu memungkinkan penyaringan berdasarkan jenis dan agak baru memungkinkan penyaringan lebih lanjut melalui klausa when . Ini sepertinya membuang-buang fitur bahasa jika kita tidak seharusnya menggunakannya.
Dapat dikatakan bahwa ada BEBERAPA biaya untuk pengecualian yang dilemparkan, dan biaya yang MUNGKIN berdampak pada kinerja dalam loop yang berat. Namun, dapat juga dikatakan bahwa biaya pengecualian akan diabaikan dalam "aplikasi yang terhubung". Biaya aktual telah diselidiki lebih dari satu dekade yang lalu: https://stackoverflow.com/a/891230/852208 Dengan kata lain, biaya koneksi dan permintaan basis data cenderung kecil dibandingkan dengan pengecualian yang dilemparkan.
Selain itu, saya ingin menentukan metode mana yang benar-benar lebih cepat. Seperti yang diharapkan tidak ada jawaban konkret.
Kode apa pun yang loop di atas kolom menjadi lebih lambat karena jumlah kolom ada. Dapat juga dikatakan bahwa kode apa pun yang bergantung pada pengecualian akan melambat tergantung pada tingkat di mana kueri gagal ditemukan.
Mengambil jawaban dari Chad Grant dan Matt Hamilton, saya menjalankan kedua metode dengan hingga 20 kolom dan hingga tingkat kesalahan 50% (OP mengindikasikan bahwa ia menggunakan dua tes ini antara procs yang berbeda, jadi saya mengasumsikan sedikitnya dua) .
Inilah hasilnya, diplot dengan LinqPad:
Zigzag di sini adalah tingkat kesalahan (kolom tidak ditemukan) dalam setiap jumlah kolom.
Lebih dari set hasil yang lebih sempit, perulangan adalah pilihan yang baik. Namun, metode GetOrdinal / Exception hampir tidak sensitif terhadap jumlah kolom dan mulai mengungguli metode perulangan tepat di sekitar 11 kolom.
Yang mengatakan saya tidak benar-benar memiliki kinerja preferensi bijaksana karena 11 kolom terdengar masuk akal karena jumlah rata-rata kolom dikembalikan ke seluruh aplikasi. Dalam kedua kasus kita berbicara tentang pecahan satu milidetik di sini.
Namun, dari aspek kesederhanaan kode, dan dukungan alias, saya mungkin akan pergi dengan rute GetOrdinal.
Berikut ini adalah tes dalam bentuk linqpad. Merasa bebas untuk mengirim ulang dengan metode Anda sendiri:
sumber
Kode ini memperbaiki masalah yang dimiliki Levitikon dengan kode mereka: (diadaptasi dari: [1]: http://msdn.microsoft.com/en-us/library/system.data.datatablereader.getschematable.aspx )
Alasan untuk mendapatkan semua nama kolom yang tidak berguna dan bukan nama kolom dari tabel Anda ... Apakah karena Anda mendapatkan nama kolom skema (yaitu nama kolom untuk tabel Skema)
CATATAN: ini sepertinya hanya mengembalikan nama kolom pertama ...
EDIT: kode yang diperbaiki yang mengembalikan nama semua kolom, tetapi Anda tidak dapat menggunakan SqlDataReader untuk melakukannya
sumber
return r.GetSchemaTable().Rows.Cast<DataRow>().Select(x => (string)x["ColumnName"]).ToList();
:)Untuk menjaga kode Anda kuat dan bersih, gunakan fungsi ekstensi tunggal, seperti ini:
sumber
Saya juga tidak mulai
GetSchemaTable
bekerja, sampai saya menemukan cara ini .Pada dasarnya saya melakukan ini:
sumber
Columns.Contains
btw tidak sensitif huruf.sumber
Dalam situasi khusus Anda (semua prosedur memiliki kolom yang sama kecuali 1 yang memiliki 1 kolom tambahan), akan lebih baik dan lebih cepat untuk memeriksa pembaca. Properti FieldCount untuk membedakannya.
Saya tahu ini adalah posting lama tetapi saya memutuskan untuk menjawab untuk membantu orang lain dalam situasi yang sama. Anda juga dapat (karena alasan kinerja) mencampur solusi ini dengan solusi solusi iterasi.
sumber
Kelas akses data saya harus kompatibel ke belakang, jadi saya mungkin mencoba mengakses kolom dalam rilis yang belum ada di database. Kami memiliki beberapa set data yang agak besar yang dikembalikan jadi saya bukan penggemar metode ekstensi yang harus mengulangi pengumpulan kolom DataReader untuk setiap properti.
Saya memiliki kelas utilitas yang membuat daftar kolom pribadi dan kemudian memiliki metode generik yang mencoba untuk menyelesaikan nilai berdasarkan nama kolom dan tipe parameter output.
Maka saya bisa memanggil kode saya seperti itu
sumber
Kunci dari seluruh masalah ada di sini :
Jika tiga baris yang direferensikan (saat ini baris 72, 73, dan 74) diambil, maka Anda dapat dengan mudah memeriksa untuk
-1
menentukan apakah kolom tidak ada.Satu-satunya cara mengatasi ini sambil memastikan kinerja asli adalah dengan menggunakan
Reflection
implementasi berbasis, seperti berikut:Usings:
Metode ekstensi berbasis Refleksi:
sumber
Anda juga dapat memanggil GetSchemaTable () di DataReader Anda jika Anda ingin daftar kolom dan Anda tidak ingin harus mendapatkan pengecualian ...
sumber
Bagaimana tentang
Mungkin tidak seefisien dalam satu lingkaran
sumber
dr.GetSchemaTable().Columns
mengandung - itu bukan yang Anda cari.Meskipun tidak ada metode terbuka untuk umum, metode memang ada di kelas internal
System.Data.ProviderBase.FieldNameLookup
yangSqlDataReader
diandalkan.Untuk mengaksesnya dan mendapatkan kinerja asli, Anda harus menggunakan ILGenerator untuk membuat metode saat runtime. Kode berikut akan memberi Anda akses langsung ke
int IndexOf(string fieldName)
dalamSystem.Data.ProviderBase.FieldNameLookup
kelas serta melakukan pembukuan yang berfungsiSqlDataReader.GetOrdinal()
sehingga tidak ada efek samping. Kode yang dihasilkan mencerminkan yang sudah adaSqlDataReader.GetOrdinal()
kecuali yang dipanggilFieldNameLookup.IndexOf()
alih-alihFieldNameLookup.GetOrdinal()
. TheGetOrdinal()
metode panggilan keIndexOf()
fungsi dan melempar pengecualian jika-1
dikembalikan, jadi kami memotong perilaku itu.sumber
ini bekerja untuk saya
sumber
Anda bisa mendapatkan detail lebih lanjut dari sini: Bisakah Anda mendapatkan nama kolom dari SqlDataReader?
sumber