Mengapa saya tidak boleh menggunakan pola repositori dengan Entity Framework?

203

Selama wawancara kerja, saya diminta untuk menjelaskan mengapa pola repositori bukan pola yang baik untuk bekerja dengan ORM seperti Entity Framework. Mengapa demikian?

StringBuilder
sumber
60
itu pertanyaan jebakan
Omu
2
Saya mungkin akan menjawab kepada pewawancara bahwa Microsoft menggunakan pola repositori sangat sering sementara mereka menunjukkan kerangka entitas: | .
Laurent Bourgault-Roy
1
Jadi apa alasan pewawancara untuk itu bukan ide yang baik?
Bob Horn
3
Fakta lucu adalah bahwa pencarian "pola repositori" di Google memberikan hasil yang sebagian besar terkait dengan Entity Framework dan cara menggunakan pola dengan EF.
Arseni Mourzenko
2
periksa blog ayende ayende.com/blog . Berdasarkan apa yang saya ketahui, dia dulu menggunakan Pola Repositori tetapi akhirnya menyerah mendukung Pola Objek Kueri
Jaime Sangcap

Jawaban:

99

Saya tidak melihat alasan mengapa pola Repositori tidak berfungsi dengan Entity Framework. Pola repositori adalah lapisan abstraksi yang Anda letakkan di lapisan akses data Anda. Lapisan akses data Anda dapat berupa apa saja, mulai dari prosedur tersimpan ADO.NET murni hingga Entity Framework atau file XML.

Dalam sistem besar, di mana Anda memiliki data yang berasal dari sumber yang berbeda (database / XML / layanan web), ada baiknya memiliki lapisan abstraksi. Pola Repositori berfungsi dengan baik dalam skenario ini. Saya tidak percaya bahwa Entity Framework cukup abstraksi untuk menyembunyikan apa yang terjadi di balik layar.

Saya telah menggunakan pola Repositori dengan Entity Framework sebagai metode lapisan akses data saya dan saya masih menghadapi masalah.

Keuntungan lain dari abstrak DbContextdengan Repositori adalah unit-testability . Anda dapat memiliki IRepositoryantarmuka Anda yang memiliki 2 implementasi, satu (Repositori nyata) yang digunakan DbContextuntuk berbicara dengan database dan yang kedua, FakeRepositoryyang dapat mengembalikan objek dalam memori / data yang diolok-olok. Ini membuat IRepositoryunit Anda dapat diuji, dengan demikian bagian lain dari kode yang digunakan IRepository.

public interface IRepository
{
  IEnumerable<CustomerDto> GetCustomers();
}
public EFRepository : IRepository
{
  private YourDbContext db;
  private EFRepository()
  {
    db = new YourDbContext();
  }
  public IEnumerable<CustomerDto> GetCustomers()
  {
    return db.Customers.Select(f=>new CustomerDto { Id=f.Id, Name =f.Name}).ToList();
  }
}
public MockRepository : IRepository
{
  public IEnumerable<CustomerDto> GetCustomers()
  {
    // to do : return a mock list of Customers
    // Or you may even use a mocking framework like Moq
  }
}

Sekarang menggunakan DI, Anda mendapatkan implementasinya

public class SomeService
{
  IRepository repo;
  public SomeService(IRepository repo)
  {
     this.repo = repo;
  }  
  public void SomeMethod()
  {
    //use this.repo as needed
  }    
}
Shyju
sumber
3
Saya tidak mengatakan itu tidak akan berfungsi, saya juga bekerja dengan pola repositori dengan EF, tetapi hari ini saya ditanya mengapa TIDAK BAIK menggunakan pola dengan DataBase, aplikasi yang menggunakan Database
2
Oke, karena ini adalah jawaban paling populer yang akan saya pilih sebagai Jawaban yang Benar
65
Kapan terakhir kali == terpopuler paling benar?
HDave
14
DbContext sudah menjadi repositori, repositori dimaksudkan sebagai abstraksi level rendah. Jika Anda ingin abstrak sumber data yang berbeda, buatlah objek untuk merepresentasikannya.
Daniel Little
7
ColacX. Kami mencoba hal itu - DBcontext tepat di layer controller - dan kami kembali ke pola repo. Dengan pola Repo, Tes Unit berubah dari ejekan DbContext besar yang selalu gagal. EF sulit untuk digunakan dan rapuh serta menghabiskan biaya penelitian untuk nuansa EF. Kami sekarang memiliki ejekan kecil sederhana dari repo. Kode lebih bersih. Pemisahan pekerjaan lebih jelas. Saya tidak lagi setuju dengan kerumunan bahwa EF sudah menjadi pola repo dan sudah dapat diuji unit.
Rhyous
435

Satu-satunya alasan terbaik untuk tidak menggunakan pola repositori dengan Entity Framework? Entity Framework sudah mengimplementasikan pola repositori. DbContextadalah UoW Anda (Unit Kerja) dan masing-masing DbSetadalah repositori. Menerapkan lapisan lain di atas ini tidak hanya berlebihan, tetapi membuat pemeliharaan lebih sulit.

Orang mengikuti pola tanpa menyadari tujuan dari pola tersebut. Dalam kasus pola repositori, tujuannya adalah untuk mengabstraksi logika kueri basis data tingkat rendah. Di masa lalu sebenarnya menulis pernyataan SQL dalam kode Anda, pola repositori adalah cara untuk memindahkan SQL dari metode individual yang tersebar di seluruh basis kode Anda dan melokalkannya di satu tempat. Memiliki ORM seperti Entity Framework, NHibernate, dll. Adalah pengganti untuk abstraksi kode ini, dan karenanya, meniadakan kebutuhan akan pola.

Namun, itu bukan ide yang buruk untuk membuat abstraksi di atas ORM Anda, hanya saja tidak serumit UoW / repostitory. Saya akan menggunakan pola layanan, tempat Anda membuat API yang dapat digunakan aplikasi Anda tanpa mengetahui atau tidak peduli apakah datanya berasal dari Entity Framework, NHibernate, atau Web API. Ini jauh lebih sederhana, karena Anda hanya menambahkan metode ke kelas layanan Anda untuk mengembalikan data yang dibutuhkan aplikasi Anda. Jika Anda menulis aplikasi Agenda, misalnya, Anda mungkin memiliki panggilan layanan untuk mengembalikan item yang jatuh tempo minggu ini dan belum selesai. Semua aplikasi Anda tahu bahwa jika menginginkan informasi ini, ia memanggil metode itu. Di dalam metode itu dan di layanan Anda secara umum, Anda berinteraksi dengan Entity Framework atau apa pun yang Anda gunakan. Kemudian, jika nanti Anda memutuskan untuk beralih ORM atau menarik info dari API Web,

Mungkin terdengar seperti itu argumen potensial untuk menggunakan pola repositori, tetapi perbedaan utama di sini adalah bahwa layanan adalah lapisan yang lebih tipis dan diarahkan untuk mengembalikan data yang sepenuhnya dipanggang, daripada sesuatu yang Anda terus query, seperti dengan gudang.

Chris Pratt
sumber
68
Tampaknya ini satu-satunya jawaban yang benar.
Mike Chamberlain
10
Anda dapat mengejek DbContextdi EF6 + (lihat: msdn.microsoft.com/en-us/data/dn314429.aspx ). Bahkan dalam versi yang lebih rendah, Anda dapat menggunakan DbContextkelas mirip palsu dengan mocked DbSets, karena DbSetmengimplementasikan iterface IDbSet,.
Chris Pratt
14
@TheZenker, Anda mungkin tidak benar-benar mengikuti pola repositori. Perbedaan yang paling ketat adalah nilai pengembalian. Repositori mengembalikan queryable, sedangkan layanan harus mengembalikan enumerables. Bahkan itu tidak benar-benar hitam dan putih, karena ada beberapa tumpang tindih di sana. Ini lebih pada bagaimana Anda menggunakannya. Repositori seharusnya mengembalikan set semua objek, yang kemudian Anda kueri lebih lanjut, sementara layanan harus mengembalikan dataset akhir, dan seharusnya tidak mendukung query lebih lanjut.
Chris Pratt
10
Dengan risiko terdengar egois: mereka salah. Sekarang, sejauh tutorial resmi berjalan, Microsoft telah mundur menggunakan repositori dari apa yang saya lihat sejak EF6. Mengenai buku itu, saya tidak dapat berbicara mengapa penulis memilih untuk menggunakan repositori. Apa yang saya dapat berbicara dengan, sebagai seseorang di parit membangun aplikasi skala besar, adalah bahwa menggunakan pola repositori dengan Kerangka Entitas adalah mimpi buruk pemeliharaan. Setelah Anda pindah ke sesuatu yang lebih kompleks dari segelintir repositori, Anda akhirnya menghabiskan banyak waktu mengelola repositori / unit kerja Anda.
Chris Pratt
6
Saya biasanya hanya memiliki satu layanan per basis data atau metode akses. Saya menggunakan metode umum untuk meminta beberapa tipe entitas dari set metode yang sama. Saya menggunakan Ninject untuk menyuntikkan konteks saya ke layanan saya dan kemudian layanan saya ke controller saya sehingga semuanya rapi dan rapi.
Chris Pratt
45

Inilah satu hal yang diambil dari Ayende Rahien: Merancang di lubang kehancuran: Kejahatan dari lapisan abstraksi repositori

Saya belum yakin apakah saya setuju dengan kesimpulannya. Ini adalah tangkapan ke-22 - di satu sisi, jika saya membungkus Konteks EF saya di repositori tipe-spesifik dengan metode pencarian data spesifik-kueri, saya benar-benar dapat menguji unit kode saya (semacam), yang hampir tidak mungkin dengan Entity Kerangka kerja saja. Di sisi lain, saya kehilangan kemampuan untuk melakukan pencarian yang kaya dan pemeliharaan hubungan semantik (tetapi bahkan ketika saya memiliki akses penuh ke fitur-fitur itu saya selalu merasa seperti berjalan di atas kulit telur di sekitar EF atau ORM lain yang mungkin saya pilih , karena saya tidak pernah tahu metode apa yang bisa didukung atau diterapkan oleh penerapan IQueryable, apakah itu akan menafsirkan saya menambahkan ke koleksi properti navigasi sebagai kreasi atau hanya sebuah asosiasi, apakah itu akan malas atau bersemangat memuat atau tidak memuat sama sekali oleh default, dll., jadi mungkin ini lebih baik. "Pemetaan" objek-relasional Nol-impedansi adalah sesuatu makhluk mitologis - mungkin itulah sebabnya rilis terbaru Kerangka Entitas diberi nama sandi "Magic Unicorn").

Namun, mengambil entitas Anda melalui metode pengambilan data khusus kueri berarti bahwa pengujian unit Anda pada dasarnya adalah tes kotak putih dan Anda tidak punya pilihan dalam hal ini, karena Anda harus tahu sebelumnya metode repositori mana yang sedang diuji oleh unit yang sedang diuji. panggilan untuk mengejeknya. Dan Anda masih belum benar-benar menguji kueri sendiri, kecuali jika Anda juga menulis tes integrasi.

Ini adalah masalah kompleks yang membutuhkan solusi kompleks. Anda tidak dapat memperbaikinya hanya dengan berpura-pura bahwa semua entitas Anda adalah tipe yang terpisah tanpa hubungan di antara mereka dan menyatukannya masing-masing ke dalam repositori mereka sendiri. Memang bisa , tapi itu menyebalkan.

Pembaruan: Saya telah cukup sukses menggunakan penyedia Upaya untuk Entity Framework. Upaya adalah penyedia di-memori (open source) yang memungkinkan Anda untuk menggunakan EF dalam tes persis seperti yang Anda gunakan terhadap database nyata. Saya sedang mempertimbangkan untuk mengalihkan semua tes dalam proyek ini. Saya sedang berupaya menggunakan penyedia ini, karena sepertinya membuat semuanya jadi lebih mudah. Ini adalah satu-satunya solusi yang saya temukan sejauh ini yang membahas semua masalah yang saya gembar-gemborkan sebelumnya. Satu-satunya hal adalah ada sedikit keterlambatan ketika memulai tes saya karena itu membuat database di memori (menggunakan paket lain yang disebut NMemory untuk melakukan ini), tapi saya tidak melihat ini sebagai masalah nyata. Ada artikel Proyek Kode yang berbicara tentang menggunakan Usaha (versus SQL CE) untuk pengujian.

luksan
sumber
3
Setiap artikel arsitektur tanpa menyebutkan unit test secara otomatis dikirim ke tempat sampah untuk saya. Salah satu titik dari pola repositori adalah untuk mendapatkan beberapa kemampuan uji.
Sleeper Smith
3
Anda masih dapat memiliki unit test tanpa membungkus Konteks EF (yang sudah menjadi repositori). Anda harus menguji unit domain / layanan Anda bukan permintaan basis data (itu adalah tes integrasi).
Daniel Little
2
Testabilitas EF telah meningkat pesat di versi 6. Sekarang Anda dapat mengejek sepenuhnya DbContext. Bagaimanapun, Anda selalu bisa mengejek DbSet, dan itulah daging Entity Framework. DbContextsedikit lebih dari sekadar kelas untuk menampung DbSetproperti Anda (repositori) di satu lokasi (unit kerja), terutama dalam konteks pengujian unit, di mana semua inisialisasi basis data dan hal-hal koneksi tidak diinginkan atau dibutuhkan.
Chris Pratt
Kehilangan navigasi entitas terkait buruk dan kontra OOP, tetapi Anda akan memiliki lebih banyak kontrol pada apa yang sedang ditanya.
Alireza
Sampai pada titik pengujian, EF Core telah hadir jauh dengan In-Memory dan In-Memory yang tidak sesuai dengan penyedia Sqlite untuk memungkinkan pengujian unit. Bawa buruh pelabuhan ketika Anda membutuhkan pengujian integrasi untuk menjalankan tes pada basis data kemas.
Sudhanshu Mishra
16

Alasan mengapa Anda mungkin melakukan itu adalah karena itu sedikit berlebihan. Entity Framework memberi Anda banyak manfaat pengkodean dan fungsional, itu sebabnya Anda menggunakannya, jika Anda kemudian mengambilnya dan membungkusnya dalam pola repositori yang Anda buang keunggulannya, Anda mungkin juga menggunakan lapisan akses data lainnya.

Sembilan Ekor
sumber
dapatkah Anda memberi tahu beberapa keunggulan untuk "Kerangka Entitas memberi Anda banyak manfaat pengkodean dan fungsional"?
ManirajSS
2
ini yang dia maksud. var id = Entity.Where (i => i.Id == 1337) .Single () dienkapsulasi dan bungkus ini dalam repositori yang pada dasarnya Anda tidak dapat melakukan logika kueri seperti ini dari luar, yang memaksa Anda untuk menambahkan kode lebih banyak ke repositori dan antarmuka untuk mengambil id. B mengembalikan konteks entitas dari repositori sehingga Anda dapat menulis logika kueri (yang hanya omong kosong)
ColacX
14

Secara teori saya pikir masuk akal untuk merangkum logika koneksi basis data agar lebih mudah digunakan kembali, tetapi seperti yang diperdebatkan di bawah, kerangka kerja modern kita pada dasarnya menangani hal ini sekarang.

Mempertimbangkan Kembali Pola Repositori

Kemegahan
sumber
Saya menyukai artikel ini, tetapi IMHO untuk aplikasi perusahaan, lapisan abstraksi antara DAL dan Bl HARUS Memiliki fitur, karena Anda tidak bisa tahu apa yang sebenarnya akan digunakan besok. Tetapi terima kasih telah berbagi tautan
1
Sementara secara pribadi saya pikir itu berlaku untuk misalnya NHibernate ( ISessionFactorydan ISessionmudah dipermainkan), itu tidak mudah dengan DbContext, sayangnya ...
Patryk Ćwiek
6

Alasan yang sangat bagus untuk menggunakan pola repositori adalah untuk memungkinkan pemisahan logika bisnis Anda dan / atau UI Anda dari System.Data.Entity. Ada banyak keuntungan untuk ini, termasuk manfaat nyata dalam pengujian unit dengan memungkinkan dia menggunakan Fakes atau Mocks.

James Culshaw
sumber
Saya setuju dengan jawaban ini. Repositori saya pada dasarnya hanya metode ekstensi, yang tidak melakukan apa pun selain membangun pohon ekspresi. Atas abstraksi SANGAT sederhana yang hanya menyediakan fungsionalitas generik langsung di atas dbcontext. Satu-satunya tujuan nyata dari abstraksi ini adalah untuk membuat IoC sedikit lebih mudah. Saya pikir orang mencoba melakukan hal-hal dalam repositori mereka yang tidak seharusnya mereka lakukan. Mereka membuat repo per entitas, atau menempatkan logika bisnis di sana yang seharusnya ada di lapisan layanan. Anda hanya benar-benar membutuhkan satu repo generik sederhana. Tidak perlu, hanya menyediakan antarmuka yang konsisten.
Brandon
Satu hal lagi yang ingin saya tambahkan. Ya CQRS adalah metodologi yang jauh lebih unggul dalam kasus PALING. Untuk beberapa klien saya telah bekerja ketika database tidak bekerja dengan baik dengan pengembang (yang terjadi lebih sering daripada yang dipikirkan orang di bank), EF over SQL adalah pilihan terbaik. Dalam skenario khusus itu, ketika Anda sama sekali tidak memiliki kendali atas database Anda, pola repositori masuk akal. Karena sangat mirip dengan struktur data dan mudah untuk menerjemahkan apa yang terjadi pada database dan sebaliknya. Ini benar-benar keputusan politik dan logistik menurut saya. Untuk menenangkan dewa DB.
Brandon
1
Saya sebenarnya mulai mempertanyakan pendapat saya sebelumnya tentang ini. EF adalah gabungan Unit Kerja dan Pola Penyimpanan. Seperti yang disebutkan oleh Chris Pratt di atas dengan EF6, Anda dapat dengan mudah mengejek objek Context dan DbSet. Saya masih percaya bahwa akses data harus dibungkus dalam kelas-kelas untuk melindungi kelas-kelas logika bisnis dari mekanisme akses data aktual, tetapi untuk pergi keseluruhan babi dan membungkus EF dengan repositori lain dan abstraksi Unit Kerja tampaknya berlebihan.
James Culshaw
Saya tidak berpikir ini adalah jawaban yang baik karena pernyataan pendukung Anda hanya bahwa ada banyak keuntungan sementara hanya mendaftar satu. Yang Anda lakukan bukan alasan yang baik karena Anda dapat menggunakan database dalam memori untuk entitas untuk melakukan pengujian unit.
Joel McBeth
@ jcmcbeth jika Anda melihat komentar saya tepat di atas Anda, Anda akan melihat bahwa saya telah mengubah pendapat awal saya sehubungan dengan pola repositori dan EF.
James Culshaw
0

Kami memiliki masalah dengan duplikat tapi berbeda contoh Entitas Kerangka DbContext ketika wadah IoC yang baru () naik repositori per jenis (misalnya contoh UserRepository dan GroupRepository yang masing-masing memanggil IDbSet mereka sendiri dari DBContext), kadang-kadang dapat menyebabkan beberapa konteks per permintaan (dalam konteks MVC / web).

Sebagian besar waktu masih berfungsi, tetapi ketika Anda menambahkan lapisan layanan di atas itu dan layanan tersebut menganggap objek yang dibuat dengan satu konteks dengan benar akan dilampirkan sebagai koleksi anak ke objek baru dalam konteks lain, kadang-kadang gagal dan kadang-kadang tidak t tergantung pada kecepatan komit.

Mufasa
sumber
Saya mengalami masalah ini di beberapa proyek yang berbeda.
ColacX
0

Setelah mencoba pola repositori pada proyek kecil saya sangat menyarankan untuk tidak menggunakannya; bukan karena itu menyulitkan sistem Anda, dan bukan karena mengejek data adalah mimpi buruk, tetapi karena pengujian Anda menjadi tidak berguna !!

Mengejek data memungkinkan Anda menambahkan detail tanpa header, menambahkan catatan yang melanggar batasan basis data, dan menghapus entitas yang akan dihapus oleh database. Di dunia nyata pembaruan tunggal dapat memengaruhi banyak tabel, log, riwayat, ringkasan, dll., Serta kolom seperti bidang tanggal yang terakhir dimodifikasi, kunci yang dibuat secara otomatis, bidang yang dihitung.

Singkatnya menjalankan tes Anda pada database nyata memberi Anda hasil nyata dan Anda dapat menguji tidak hanya layanan dan antarmuka Anda tetapi juga perilaku database. Anda dapat memeriksa apakah prosedur tersimpan Anda melakukan hal yang benar dengan data, mengembalikan hasil yang diharapkan, atau bahwa catatan yang Anda kirim untuk dihapus benar-benar dihapus! Tes semacam itu juga dapat mengekspos masalah seperti lupa untuk meningkatkan kesalahan dari prosedur tersimpan, dan ribuan skenario seperti itu.

Saya pikir kerangka kerja entitas menerapkan pola repositori lebih baik daripada artikel yang saya baca sejauh ini dan itu jauh melampaui apa yang mereka coba capai.

Repositori adalah praktik terbaik pada hari-hari di mana kami menggunakan XBase, AdoX, dan Ado.Net, tetapi dengan entitas !! (Repositori atas repositori)

Terakhir saya pikir terlalu banyak orang menginvestasikan banyak waktu untuk belajar dan menerapkan pola penyimpanan dan mereka menolak untuk melepaskannya. Sebagian besar membuktikan kepada diri mereka sendiri bahwa mereka tidak membuang waktu mereka.

Zawer Haroon
sumber
1
Kecuali bahwa Anda TIDAK ingin menguji perilaku basis data Anda dalam unit test, karena ini sama sekali bukan tingkat pengujian.
Mariusz Jamro
Ya, yang Anda bicarakan di sini adalah pengujian integrasi , dan itu memang berharga, tetapi unit test sama sekali berbeda. Tes unit Anda tidak boleh mengenai basis data nyata, tetapi Anda disarankan untuk menambahkan pengujian integrasi.
Chris Pratt
-3

Karena Migrasi: Migrasi tidak dapat berfungsi, karena string koneksi berada di web.config. Tapi, DbContext berada di lapisan Repositori. IDbContextFactory perlu memiliki string konfigurasi ke database. Tetapi tidak ada cara migrasi mendapatkan string koneksi dari web.config.

Ada pekerjaan di sekitar tetapi saya belum menemukan solusi bersih untuk ini!

Guntur
sumber