Saya telah melalui banyak jalan dan membuat banyak implementasi repositori pada proyek yang berbeda dan ... Saya telah menyerah dan menyerah, inilah alasannya.
Pengkodean untuk pengecualian
Apakah Anda membuat kode untuk 1% kemungkinan database Anda akan berubah dari satu teknologi ke teknologi lainnya? Jika Anda berpikir tentang keadaan bisnis Anda di masa depan dan mengatakan ya, itu adalah kemungkinannya, maka a) mereka harus memiliki banyak uang untuk melakukan migrasi ke teknologi DB lain atau b) Anda memilih teknologi DB untuk bersenang-senang atau c ) Ada yang tidak beres dengan teknologi pertama yang Anda putuskan untuk digunakan.
Mengapa membuang sintaks LINQ yang kaya?
LINQ dan EF dikembangkan sehingga Anda dapat melakukan hal-hal yang rapi dengannya untuk membaca dan melintasi grafik objek. Membuat dan memelihara repositori yang dapat memberi Anda fleksibilitas yang sama untuk melakukannya adalah tugas yang mengerikan. Dalam pengalaman saya, setiap kali saya membuat repositori, saya SELALU mengalami kebocoran logika bisnis ke lapisan repositori untuk membuat kueri lebih berkinerja dan / atau mengurangi jumlah klik ke database.
Saya tidak ingin membuat metode untuk setiap permutasi kueri yang harus saya tulis. Saya mungkin juga menulis prosedur tersimpan. Saya tidak ingin GetOrder
, GetOrderWithOrderItem
, GetOrderWithOrderItemWithOrderActivity
, GetOrderByUserId
, dan seterusnya ... Saya hanya ingin mendapatkan entitas utama dan melintasi dan termasuk objek grafik seperti yang saya jadi tolong.
Sebagian besar contoh repositori adalah omong kosong
Kecuali jika Anda sedang mengembangkan sesuatu yang BENAR-BENAR sederhana seperti blog atau sesuatu yang pertanyaan Anda tidak akan pernah sesederhana 90% dari contoh yang Anda temukan di internet seputar pola repositori. Saya tidak bisa cukup menekankan ini! Ini adalah sesuatu yang seseorang harus merangkak melalui lumpur untuk mengetahuinya. Akan selalu ada satu kueri yang merusak repositori / solusi yang telah Anda buat, dan tidak sampai titik di mana Anda menebak-nebak diri Anda sendiri dan hutang / erosi teknis dimulai.
Jangan uji unit saya bro
Tetapi bagaimana dengan pengujian unit jika saya tidak memiliki repositori? Bagaimana saya akan mengejek? Sederhana saja. Mari kita lihat dari kedua sudut:
Tanpa repositori - Anda dapat memalsukan DbContext
penggunaan IDbContext
atau beberapa trik lain tetapi kemudian Anda benar-benar menguji unit LINQ ke Objek dan bukan LINQ ke Entitas karena kueri ditentukan saat runtime ... Oke jadi itu tidak bagus! Jadi sekarang terserah pada uji integrasi untuk membahasnya.
Dengan repositori - Anda sekarang dapat memalsukan repositori Anda dan unit menguji lapisan di antaranya. Bagus kan? Sebenarnya tidak ... Dalam kasus di atas di mana Anda harus membocorkan logika ke lapisan repositori untuk membuat kueri lebih berkinerja dan / atau lebih sedikit klik ke database, bagaimana pengujian unit Anda dapat mencakupnya? Sekarang ada di lapisan repo dan Anda tidak ingin mengujinya, IQueryable<T>
bukan? Jujur juga, pengujian unit Anda tidak akan mencakup kueri yang memiliki .Where()
klausa 20 baris dan.Include()
Banyak hubungan dan hits database lagi untuk melakukan semua hal lain ini, bla, bla, bla lagian karena kueri dihasilkan saat runtime. Juga karena Anda membuat repositori untuk menjaga ketekunan lapisan atas tidak diketahui, jika Anda sekarang ingin mengubah teknologi database, maaf pengujian unit Anda pasti tidak akan menjamin hasil yang sama pada waktu proses, kembali ke pengujian integrasi. Jadi seluruh isi repositori tampak aneh ..
2 sen
Kami sudah kehilangan banyak fungsi dan sintaks saat menggunakan EF daripada prosedur tersimpan biasa (penyisipan massal, penghapusan massal, CTE, dll.) Tetapi saya juga membuat kode dalam C # jadi saya tidak perlu mengetik biner. Kami menggunakan EF sehingga kami dapat memiliki kemungkinan untuk menggunakan penyedia yang berbeda dan untuk bekerja dengan grafik objek dengan cara terkait yang bagus di antara banyak hal. Abstraksi tertentu berguna dan ada juga yang tidak.
Coding for the exception
: Menggunakan repositori tidak dapat mengganti mesin database. Ini tentang memisahkan bisnis dari kegigihan.DbSet
adalah repositori , danDbContext
merupakan Unit Kerja . Mengapa menerapkan pola repositori ketika ORM sudah melakukannya untuk kami! Untuk pengujian, cukup ubah penyedia keInMemory
. Dan lakukan tes Anda! Itu didokumentasikan dengan baik di MSDN.Pola repositori adalah abstraksi . Tujuannya adalah untuk mengurangi kompleksitas dan membuat sisa kode tetap tidak peduli. Sebagai bonus, ini memungkinkan Anda menulis pengujian unit, bukan pengujian integrasi .
Masalahnya adalah bahwa banyak pengembang gagal memahami tujuan pola dan membuat repositori yang membocorkan informasi spesifik persistensi hingga pemanggil (biasanya dengan mengekspos
IQueryable<T>
). Dengan melakukan itu mereka tidak mendapatkan keuntungan atas penggunaan OR / M secara langsung.Perbarui untuk menjawab jawaban lain
Pengkodean untuk pengecualian
Menggunakan repositori bukanlah tentang kemampuan untuk mengganti teknologi persistensi (yaitu mengubah database atau menggunakan layanan web, dll.). Ini tentang memisahkan logika bisnis dari ketekunan untuk mengurangi kompleksitas dan keterkaitan.
Tes unit vs tes integrasi
Anda tidak menulis pengujian unit untuk repositori. Titik.
Tetapi dengan memperkenalkan repositori (atau lapisan abstraksi lainnya antara persistensi dan bisnis), Anda dapat menulis pengujian unit untuk logika bisnis. yaitu Anda tidak perlu khawatir tentang pengujian Anda yang gagal karena database yang tidak dikonfigurasi dengan benar.
Adapun pertanyaannya. Jika Anda menggunakan LINQ, Anda juga harus memastikan bahwa kueri Anda berfungsi, seperti yang harus Anda lakukan dengan repositori. dan itu dilakukan dengan menggunakan uji integrasi.
Perbedaannya adalah jika Anda belum mencampurkan bisnis Anda dengan pernyataan LINQ, Anda dapat 100% yakin bahwa kode ketekunan Anda yang gagal dan bukan yang lainnya.
Jika Anda menganalisis pengujian Anda, Anda juga akan melihat bahwa pengujian tersebut jauh lebih bersih jika Anda tidak memiliki kekhawatiran yang campur aduk (yaitu logika LINQ + Bisnis)
Contoh repositori
Kebanyakan contoh adalah omong kosong. itu benar sekali. Namun, jika Anda mencari pola desain di Google, Anda akan menemukan banyak contoh jelek. Tidak ada alasan untuk menghindari penggunaan pola.
Membangun implementasi repositori yang benar sangatlah mudah. Faktanya, Anda hanya perlu mengikuti satu aturan:
Jangan menambahkan apapun ke dalam kelas repositori hingga saat Anda membutuhkannya
Banyak pembuat kode malas dan mencoba membuat repositori generik dan menggunakan kelas dasar dengan banyak metode yang mungkin mereka perlukan. YAGNI. Anda menulis kelas repositori sekali dan menyimpannya selama aplikasi hidup (bisa bertahun-tahun). Mengapa mengacaukannya dengan menjadi malas. Tetap bersih tanpa warisan kelas dasar. Ini akan membuatnya lebih mudah untuk dibaca dan dipelihara.
(Pernyataan di atas adalah pedoman dan bukan hukum. Kelas dasar bisa sangat termotivasi. Coba pikirkan sebelum menambahkannya, sehingga Anda menambahkannya untuk alasan yang benar)
Barang lama
Kesimpulan:
Jika Anda tidak keberatan memiliki pernyataan LINQ dalam kode bisnis Anda atau peduli tentang pengujian unit, saya tidak melihat alasan untuk tidak menggunakan Entity Framework secara langsung.
Memperbarui
Saya telah membuat blog tentang pola repositori dan apa arti sebenarnya dari "abstraksi": http://blog.gauffin.org/2013/01/repository-pattern-done-right/
Perbarui 2
Sekali lagi: Entitas dengan 20+ kolom tidak dimodelkan dengan benar. Itu adalah entitas ALLAH. Memecahnya.
Saya tidak berdebat bahwa
IQueryable
itu tidak dibuat untuk quering. Saya mengatakan bahwa itu tidak tepat untuk lapisan abstraksi seperti pola Repositori karena itu bocor. Tidak ada penyedia LINQ To Sql yang 100% lengkap (seperti EF).Mereka semua memiliki implementasi hal-hal spesifik seperti bagaimana menggunakan eager / lazy loading atau bagaimana melakukan pernyataan SQL "IN". Mengekspos
IQueryable
di repositori memaksa pengguna untuk mengetahui semua hal itu. Dengan demikian seluruh upaya untuk mengabstraksi sumber data adalah kegagalan total. Anda hanya menambahkan kompleksitas tanpa mendapatkan keuntungan apa pun atas penggunaan OR / M secara langsung.Terapkan pola Repositori dengan benar atau jangan gunakan sama sekali.
(Jika Anda benar-benar ingin menangani entitas besar, Anda dapat menggabungkan pola Repositori dengan pola Spesifikasi . Itu memberi Anda abstraksi lengkap yang juga dapat diuji.)
sumber
IMO baik
Repository
abstraksi maupunUnitOfWork
abstraksi memiliki tempat yang sangat berharga dalam perkembangan yang berarti. Orang akan berdebat tentang detail implementasi, tetapi seperti ada banyak cara untuk menguliti kucing, ada banyak cara untuk menerapkan abstraksi.Pertanyaan Anda secara khusus digunakan atau tidak dan mengapa.
Karena Anda pasti menyadari bahwa Anda sudah memiliki kedua pola ini yang dibangun ke dalam Entity Framework,
DbContext
is theUnitOfWork
danDbSet
is theRepository
. Anda biasanya tidak perlu menguji unitUnitOfWork
atauRepository
dirinya sendiri karena mereka hanya memfasilitasi antara kelas Anda dan implementasi akses data yang mendasarinya. Apa yang perlu Anda lakukan, lagi dan lagi, adalah mengejek kedua abstraksi ini saat unit menguji logika layanan Anda.Anda dapat memalsukan, memalsukan, atau apa pun dengan pustaka eksternal yang menambahkan lapisan dependensi kode (yang tidak Anda kendalikan) antara logika yang melakukan pengujian dan logika yang sedang diuji.
Jadi poin kecilnya adalah memiliki abstraksi Anda sendiri
UnitOfWork
danRepository
memberi Anda kontrol dan fleksibilitas maksimum saat mengejek pengujian unit Anda.Semuanya baik-baik saja, tetapi bagi saya, kekuatan sebenarnya dari abstraksi ini adalah menyediakan cara sederhana untuk menerapkan teknik Pemrograman Berorientasi Aspek dan mematuhi prinsip SOLID .
Jadi, Anda memiliki
IRepository
:Dan implementasinya:
Sejauh ini tidak ada yang luar biasa, tetapi sekarang kami ingin menambahkan beberapa logging - mudah dengan Dekorator logging .
Semua selesai dan tanpa perubahan pada kode kami yang ada . Ada banyak masalah lintas sektoral lain yang dapat kami tambahkan, seperti penanganan pengecualian, cache data, validasi data, atau apa pun, dan di seluruh proses desain dan pembuatan kami, hal paling berharga yang kami miliki yang memungkinkan kami menambahkan fitur sederhana tanpa mengubah kode yang ada. adalah
IRepository
abstraksi kami .Sekarang, berkali-kali saya telah melihat pertanyaan ini di StackOverflow - "bagaimana Anda membuat Entity Framework berfungsi di lingkungan multi tenant?".
https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant
Jika Anda memiliki
Repository
abstraksi maka jawabannya adalah “mudah menambahkan dekorator”IMO Anda harus selalu menempatkan abstraksi sederhana di atas komponen pihak ketiga yang akan dirujuk di lebih dari beberapa tempat. Dari perspektif ini, ORM adalah kandidat yang tepat karena direferensikan di banyak kode kami.
Jawaban yang biasanya terlintas dalam pikiran ketika seseorang berkata "mengapa saya harus memiliki abstraksi (misalnya
Repository
) atas perpustakaan pihak ketiga ini atau itu" adalah "mengapa Anda tidak?"Dekorator PS sangat mudah diterapkan menggunakan Kontainer IoC, seperti SimpleInjector .
sumber
Pertama-tama, seperti yang disarankan oleh beberapa jawaban, EF sendiri adalah pola repositori, tidak perlu membuat abstraksi lebih lanjut hanya untuk menamainya sebagai repositori.
Repositori Mockable untuk Pengujian Unit, apakah kita benar-benar membutuhkannya?
Kami membiarkan EF berkomunikasi untuk menguji DB dalam tes unit untuk menguji logika bisnis kami langsung terhadap DB tes SQL. Saya sama sekali tidak melihat keuntungan dari meniru pola repositori apa pun. Apa yang salah melakukan pengujian unit terhadap database pengujian? Karena ini adalah operasi massal tidak dimungkinkan dan kami akhirnya menulis SQL mentah. SQLite dalam memori adalah kandidat yang tepat untuk melakukan pengujian unit terhadap database nyata.
Abstraksi yang Tidak Diperlukan
Apakah Anda ingin membuat repositori agar di masa mendatang Anda dapat dengan mudah mengganti EF dengan NHbibernate dll atau yang lainnya? Kedengarannya rencana yang bagus, tetapi apakah ini benar-benar hemat biaya?
Linq membunuh tes unit?
Saya ingin melihat contoh bagaimana itu bisa membunuh.
Injeksi Ketergantungan, IoC
Wow, ini kata-kata yang bagus, pasti terlihat bagus secara teori, tetapi terkadang Anda harus memilih pertukaran antara desain hebat dan solusi hebat. Kami memang menggunakan semua itu, dan akhirnya kami membuang semuanya dan memilih pendekatan yang berbeda. Ukuran vs Kecepatan (Ukuran kode dan Kecepatan pengembangan) sangat penting dalam kehidupan nyata. Pengguna membutuhkan fleksibilitas, mereka tidak peduli jika kode Anda bagus dalam desain dalam hal DI atau IoC.
Kecuali Anda sedang membangun Visual Studio
Semua desain hebat ini diperlukan jika Anda membangun program kompleks seperti Visual Studio atau Eclipse yang akan dikembangkan oleh banyak orang dan harus sangat dapat disesuaikan. Semua pola pengembangan yang hebat muncul setelah bertahun-tahun pengembangan yang dilakukan oleh IDE ini, dan mereka telah berkembang di tempat di mana semua pola desain yang hebat ini sangat berarti. Tetapi jika Anda melakukan penggajian berbasis web sederhana, atau aplikasi bisnis sederhana, lebih baik Anda mengembangkannya seiring waktu, daripada menghabiskan waktu untuk membangunnya untuk jutaan pengguna yang hanya akan digunakan untuk 100 pengguna.
Repositori sebagai Tampilan Tersaring - ISecureRepository
Di sisi lain, repositori harus berupa tampilan EF yang difilter yang menjaga akses ke data dengan menerapkan pengisi yang diperlukan berdasarkan pengguna / peran saat ini.
Tetapi melakukan hal itu semakin memperumit repositori karena berakhir dengan basis kode yang sangat besar untuk dipelihara. Orang-orang akhirnya membuat repositori yang berbeda untuk jenis pengguna yang berbeda atau kombinasi jenis entitas. Tidak hanya itu, kami juga memiliki banyak DTO.
Jawaban berikut adalah contoh implementasi Filtered Repository tanpa membuat seluruh rangkaian kelas dan metode. Ini mungkin tidak menjawab pertanyaan secara langsung tetapi dapat berguna dalam menurunkannya.
Penafian: Saya penulis Entity REST SDK.
http://entityrestsdk.codeplex.com
Dengan mengingat di atas, kami mengembangkan SDK yang membuat repositori tampilan yang difilter berdasarkan SecurityContext yang menampung filter untuk operasi CRUD. Dan hanya dua jenis aturan yang menyederhanakan operasi kompleks apa pun. Pertama adalah akses ke entitas, dan lainnya adalah aturan Baca / Tulis untuk properti.
Keuntungannya adalah, Anda tidak menulis ulang logika bisnis atau repositori untuk jenis pengguna yang berbeda, Anda cukup memblokir atau memberi mereka akses.
Aturan LINQ ini dievaluasi terhadap Database dalam metode SaveChanges untuk setiap operasi, dan Aturan ini bertindak sebagai Firewall di depan Database.
sumber
Ada banyak perdebatan tentang metode mana yang benar, jadi saya melihatnya karena keduanya dapat diterima jadi saya menggunakan yang mana yang paling saya sukai (Yang bukan repositori, UoW).
Dalam EF UoW diimplementasikan melalui DbContext dan DbSets adalah repositori.
Adapun cara bekerja dengan lapisan data saya hanya langsung mengerjakan objek DbContext, untuk kueri kompleks saya akan membuat metode ekstensi untuk kueri yang dapat digunakan kembali.
Saya yakin Ayende juga memiliki beberapa posting tentang bagaimana mengabstraksi operasi CUD itu buruk.
Saya selalu membuat antarmuka dan memiliki konteks yang diwarisi darinya sehingga saya dapat menggunakan wadah IoC untuk DI.
sumber
Apa yang paling diterapkan di EF bukanlah Pola Repositori. Ini adalah pola Fasad (mengabstraksi panggilan ke metode EF menjadi versi yang lebih sederhana dan lebih mudah digunakan).
EF adalah yang menerapkan Pola Repositori (dan pola Unit Kerja juga). Artinya, EF adalah yang mengabstraksi lapisan akses data sehingga pengguna tidak tahu bahwa mereka berurusan dengan SQLServer.
Dan pada saat itu, sebagian besar "repositori" di EF bahkan bukan Fasad yang bagus karena mereka hanya memetakan, secara langsung, ke metode tunggal di EF, bahkan sampai memiliki tanda tangan yang sama.
Dua alasan, kemudian, untuk menerapkan apa yang disebut pola "Repositori" di atas EF adalah untuk memungkinkan pengujian yang lebih mudah dan untuk membuat subset dari panggilan "terekam" untuk itu. Tidak buruk dalam dirinya sendiri, tetapi jelas bukan Repositori.
sumber
Linq adalah 'Tempat Penyimpanan' saat ini.
ISession + Linq sudah menjadi repositori, dan Anda tidak memerlukan
GetXByY
metode maupunQueryData(Query q)
generalisasi. Menjadi sedikit paranoid terhadap penggunaan DAL, saya masih lebih suka antarmuka repositori. (Dari sudut pandang pemeliharaan kita juga masih harus memiliki beberapa fasad di atas antarmuka akses data tertentu).Ini adalah repositori yang kita gunakan - ia memisahkan kita dari penggunaan langsung nhibernate, tetapi menyediakan antarmuka linq (sebagai akses ISession dalam kasus luar biasa, yang pada akhirnya tunduk pada refactor).
sumber
The Repository (atau namun satu lagi memilih untuk menyebutnya) saat ini bagi saya adalah kebanyakan tentang abstrak pergi lapisan ketekunan.
Saya menggunakannya digabungkan dengan objek kueri jadi saya tidak memiliki kopling ke teknologi tertentu dalam aplikasi saya. Dan juga sangat memudahkan pengujian.
Jadi, saya cenderung punya
Mungkin menambahkan metode asinkron dengan callback sebagai delegasi. Repo ini mudah diimplementasikan secara umum , jadi saya tidak bisa menyentuh garis implementasi dari aplikasi ke aplikasi. Ya, ini benar setidaknya saat menggunakan NH, saya juga melakukannya dengan EF, tapi membuat saya membenci EF. 4. Percakapan adalah awal dari sebuah transaksi. Sangat keren jika beberapa kelas berbagi instance repositori. Juga, untuk NH, satu repo dalam implementasi saya sama dengan satu sesi yang dibuka pada permintaan pertama.
Kemudian Objek Query
Untuk konfigurasi yang saya gunakan di NH hanya untuk lulus di ISession. Di EF tidak masuk akal lebih atau kurang.
Contoh kueri akan menjadi .. (NH)
Untuk melakukan kueri EF Anda harus memiliki konteks dalam basis Abstrak, bukan sesi. Tapi tentu saja ifc akan sama.
Dengan cara ini, kueri itu sendiri dikemas, dan mudah diuji. Yang terbaik dari semuanya, kode saya hanya bergantung pada antarmuka. Semuanya sangat bersih. Objek domain (bisnis) hanyalah itu, misalnya tidak ada pencampuran tanggung jawab seperti saat menggunakan pola rekaman aktif yang hampir tidak dapat diuji dan mencampur kode akses data (kueri) dalam objek domain dan dalam melakukan itu mencampur masalah (objek yang mengambil diri??). Semua orang masih bebas membuat POCO untuk transfer data.
Secara keseluruhan, banyak penggunaan kembali kode dan kesederhanaan disediakan dengan pendekatan ini tanpa kehilangan apa pun yang dapat saya bayangkan. Ada ide?
Dan terima kasih banyak kepada Ayende untuk postingannya yang bagus dan dedikasinya yang berkelanjutan. Ini adalah idenya di sini (objek kueri), bukan milik saya.
sumber
Bagi saya, ini keputusan sederhana, dengan faktor yang relatif sedikit. Faktor-faktor tersebut adalah:
Jadi, jika aplikasi saya tidak dapat membenarkan # 2, domain terpisah dan model data, maka saya biasanya tidak akan peduli dengan # 5.
sumber