Selama wawancara kerja, saya diminta untuk menjelaskan mengapa pola repositori bukan pola yang baik untuk bekerja dengan ORM seperti Entity Framework. Mengapa demikian?
Selama wawancara kerja, saya diminta untuk menjelaskan mengapa pola repositori bukan pola yang baik untuk bekerja dengan ORM seperti Entity Framework. Mengapa demikian?
Jawaban:
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 DbContext
dengan Repositori adalah unit-testability . Anda dapat memiliki IRepository
antarmuka Anda yang memiliki 2 implementasi, satu (Repositori nyata) yang digunakan DbContext
untuk berbicara dengan database dan yang kedua, FakeRepository
yang dapat mengembalikan objek dalam memori / data yang diolok-olok. Ini membuat IRepository
unit 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
}
}
Satu-satunya alasan terbaik untuk tidak menggunakan pola repositori dengan Entity Framework? Entity Framework sudah mengimplementasikan pola repositori. DbContext
adalah UoW Anda (Unit Kerja) dan masing-masing DbSet
adalah 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.
DbContext
di EF6 + (lihat: msdn.microsoft.com/en-us/data/dn314429.aspx ). Bahkan dalam versi yang lebih rendah, Anda dapat menggunakan DbContext
kelas mirip palsu dengan mocked DbSet
s, karena DbSet
mengimplementasikan iterface IDbSet
,.
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.
DbContext
. Bagaimanapun, Anda selalu bisa mengejek DbSet
, dan itulah daging Entity Framework. DbContext
sedikit lebih dari sekadar kelas untuk menampung DbSet
properti 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.
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.
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.
ISessionFactory
dan ISession
mudah dipermainkan), itu tidak mudah dengan DbContext
, sayangnya ...
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.
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.
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.
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!