Saya telah melihat banyak proyek yang memiliki repositori yang mengembalikan instance IQueryable
. Ini memungkinkan filter tambahan dan penyortiran dapat dilakukan IQueryable
oleh kode lain, yang diterjemahkan menjadi SQL yang berbeda yang dihasilkan. Saya ingin tahu dari mana pola ini berasal dan apakah itu ide yang bagus.
Kekhawatiran terbesar saya adalah bahwa sebuah IQueryable
janji untuk mencapai database beberapa waktu kemudian, ketika disebutkan. Ini berarti bahwa kesalahan akan terjadi di luar repositori. Ini bisa berarti pengecualian Entity Framework dilemparkan ke dalam lapisan aplikasi yang berbeda.
Saya juga mengalami masalah dengan Multiple Active Result Set (MARS) di masa lalu (terutama ketika menggunakan transaksi) dan pendekatan ini sepertinya akan menyebabkan hal ini terjadi lebih sering.
Saya selalu menelepon AsEnumerable
atau ToArray
di akhir setiap ekspresi LINQ saya untuk memastikan database terkena sebelum meninggalkan kode repositori.
Saya bertanya-tanya apakah pengembalian IQueryable
dapat berguna sebagai blok bangunan untuk lapisan data. Saya telah melihat beberapa kode yang sangat mewah dengan satu repositori memanggil repositori lain untuk membangun yang lebih besar lagi IQueryable
.
sumber
where
klausul untuk ditangguhkanIQueryable
, Anda hanya perlu mengirim bahwa data melalui kawat, tidak seluruh hasil set.Jawaban:
Mengembalikan IQueryable pasti akan memberikan lebih banyak fleksibilitas kepada konsumen repositori. Ini menempatkan tanggung jawab mempersempit hasil kepada klien, yang secara alami dapat bermanfaat sekaligus sebagai penopang.
Sisi baiknya, Anda tidak perlu membuat banyak metode repositori (setidaknya pada layer ini) - GetAllActiveItems, GetAllNonActiveItems, dll - untuk mendapatkan data yang Anda inginkan. Tergantung pada preferensi Anda, sekali lagi, ini bisa baik atau buruk. Anda akan (harus) mendefinisikan kontrak perilaku yang dipatuhi oleh implementasi Anda, tetapi kemana Anda akan pergi.
Jadi Anda bisa meletakkan logika pencarian berpasir di luar repositori dan membiarkannya digunakan sesuai keinginan pengguna. Jadi mengekspos IQueryable memberikan fleksibilitas yang paling dan memungkinkan untuk query yang efisien dibandingkan dengan penyaringan dalam memori, dll, dan dapat mengurangi kebutuhan untuk membuat satu ton metode pengambilan data tertentu.
Di sisi lain, sekarang Anda telah memberi pengguna Anda senapan. Mereka dapat melakukan hal-hal yang mungkin tidak Anda maksudkan (terlalu sering menggunakan .include (), melakukan permintaan berat yang berat dan melakukan penyaringan dalam memori dalam implementasi masing-masing, dll), yang pada dasarnya akan meminggirkan kontrol pelapisan dan perilaku karena Anda telah memberikan akses penuh.
Jadi tergantung pada tim, pengalaman mereka, ukuran aplikasi, layering keseluruhan dan arsitektur ... itu tergantung: - \
sumber
Expression<Func<Foo,bool>>
ke metode Anda. Parameter ini dapat diteruskan keWhere
metode secara langsung, sehingga memungkinkan konsumen memilih filter mereka tanpa perlu akses langsung ke <Foo> IQueryable.Mengekspos IQueryable ke antarmuka publik bukanlah praktik yang baik. Alasannya mendasar: Anda tidak dapat memberikan realisasi IQueryable seperti yang dikatakan.
Antarmuka publik adalah kontrak antara penyedia dan klien. Dan dalam kebanyakan kasus ada harapan implementasi penuh. IQueryable adalah cheat:
Pola repositori memberi kita representasi yang jelas dengan melapisi, dan, yang paling penting, independensi realisasi. Jadi kita bisa mengubah sumber data di masa depan.
Pertanyaannya adalah " Haruskah ada kemungkinan substitusi sumber data? ".
Jika besok bagian dari data dapat dipindahkan ke layanan SAP, basis data NoSQL, atau hanya ke file teks, dapatkah kami menjamin implementasi yang tepat dari antarmuka IQueryable?
( lebih banyak poin bagus tentang mengapa ini adalah praktik buruk)
sumber
Secara realistis, Anda memiliki tiga alternatif jika Anda ingin eksekusi yang ditangguhkan:
IQueryable
.GetCustomersInAlaskaWithChildren
, di bawah)IQueryable
internal.Saya lebih suka yang ketiga (meskipun saya sudah membantu rekan menerapkan yang kedua juga), tetapi jelas ada beberapa pengaturan dan pemeliharaan yang terlibat. (T4 untuk menyelamatkan!)
Sunting : Untuk menjernihkan kebingungan seputar apa yang saya bicarakan, pertimbangkan contoh berikut yang dicuri dari IQueryable Can Kill Your Dog, Steal Your Wife, Kill Your Will to Live, dll.
Dalam skenario di mana Anda akan mengekspos sesuatu seperti ini:
API yang saya bicarakan akan memungkinkan Anda mengekspos metode yang akan memungkinkan Anda mengekspresikan
GetCustomersInAlaskaWithChildren
(atau kombinasi kriteria lainnya) secara fleksibel, dan repo akan menjalankannya sebagai IQueryable dan mengembalikan hasilnya kepada Anda. Tetapi yang penting adalah bahwa ia berjalan di dalam lapisan repositori, sehingga masih mengambil keuntungan dari eksekusi yang ditangguhkan. Setelah Anda mendapatkan hasilnya kembali, Anda masih dapat LINQ-to-objek di dalamnya untuk sesuka hati Anda.Keuntungan lain dari pendekatan seperti ini adalah, karena mereka adalah kelas POCO, repositori ini dapat hidup di belakang layanan web atau WCF; itu bisa diekspos ke AJAX atau penelepon lain yang tidak tahu apa-apa tentang LINQ.
sumber
IQueryable
tanpa mengeksposIQueryable
itu sendiri . Bagaimana Anda akan melakukannya, dengan API bawaan?Hanya ada satu jawaban yang sah: itu tergantung pada bagaimana repositori akan digunakan.
Pada satu titik repositori Anda adalah pembungkus yang sangat tipis di sekitar DBContext sehingga Anda dapat menyuntikkan veneer testability di sekitar aplikasi berbasis database. Benar-benar tidak ada harapan dunia nyata bahwa ini dapat digunakan dalam cara yang terputus tanpa DB LINQ ramah di belakangnya karena aplikasi CRUD Anda tidak akan pernah membutuhkan itu. Maka tentu saja, mengapa tidak menggunakan IQueryable? Saya mungkin lebih suka IEnumarable karena Anda mendapatkan sebagian besar manfaat [baca: eksekusi tertunda] dan tidak terasa kotor.
Kecuali jika Anda duduk pada posisi ekstrem itu, saya akan berusaha keras untuk mengambil keuntungan dari semangat pola repositori dan mengembalikan koleksi terwujud yang sesuai yang tidak memiliki implikasi koneksi database yang mendasarinya.
sumber
IEnumerable
, penting untuk dicatat bahwa((IEnumerable<T>)query).LastOrDefault()
ini sangat berbeda dariquery.LastOrDefault()
(anggapquery
sebagaiIQueryable
). Jadi, masuk akal untuk kembaliIEnumerable
jika Anda akan mengulangi semua elemen.TIDAK jika Anda ingin melakukan pengembangan / unittesting testdriven karena tidak ada cara mudah untuk membuat repositori tiruan untuk menguji businesslogic Anda (= repositori-konsumen) dalam isolasi dari database atau penyedia IQueryable lainnya.
sumber
Dari sudut pandang arsitektur murni, IQueryable adalah abstraksi yang bocor. Secara umum, ini memberi pengguna kekuatan terlalu banyak. Yang sedang berkata, ada tempat di mana itu masuk akal. Jika Anda menggunakan OData, IQueryable membuatnya sangat mudah untuk memberikan titik akhir yang mudah difilter, disortir, dapat dikelompokkan ... dll. Saya sebenarnya lebih suka membuat DTO yang dipetakan oleh entitas saya dan sebaliknya IQueryable mengembalikan DTO. Dengan begitu saya melakukan pra-filter data saya dan benar-benar hanya menggunakan metode IQueryable untuk memungkinkan kustomisasi klien dari hasil.
sumber
Saya telah menghadapi pilihan seperti itu juga.
Jadi, mari kita simpulkan sisi positif dan negatif:
Positif:
Negatif:
Saya akan merekomendasikan untuk tidak menggunakan pendekatan ini. Sebaiknya pertimbangkan membuat beberapa Spesifikasi dan menerapkan metode repositori, yang dapat meminta basis data dengan menggunakannya. Pola spesifikasi adalah cara terbaik untuk merangkum serangkaian kondisi.
sumber
Saya pikir mengekspos IQueryable pada repositori Anda dapat diterima selama fase awal pengembangan. Ada alat UI yang dapat bekerja dengan IQueryable secara langsung dan menangani pengurutan, pemfilteran, pengelompokan, dll.
Yang sedang berkata, saya pikir hanya mengekspos mentah-mentah di repositori dan menyebutnya sehari tidak bertanggung jawab. Memiliki operasi yang bermanfaat dan bantuan kueri untuk kueri umum memungkinkan Anda memusatkan logika untuk kueri sembari juga memaparkan permukaan antarmuka yang lebih kecil kepada konsumen repositori.
Untuk beberapa iterasi pertama suatu proyek, mengekspos IQueryable secara publik di repositori memungkinkan untuk pertumbuhan yang lebih cepat. Sebelum rilis penuh pertama, saya akan merekomendasikan menjadikan operasi kueri menjadi pribadi atau terlindungi dan membawa kueri liar di bawah satu atap.
sumber
Apa yang saya lihat dilakukan (dan apa yang saya laksanakan sendiri) adalah sebagai berikut:
Apa yang memungkinkan Anda lakukan adalah memiliki fleksibilitas melakukan
IQueryable.Where(a => a.SomeFilter)
kueri pada repo Anda dengan melakukannyaconcreteRepo.GetAll(a => a.SomeFilter)
tanpa memaparkan bisnis LINQy apa pun.sumber