Sedang membaca beberapa artikel tentang keuntungan membuat Gudang Umum untuk aplikasi baru ( contoh ). Idenya tampak bagus karena memungkinkan saya menggunakan repositori yang sama untuk melakukan beberapa hal untuk beberapa tipe entitas yang berbeda sekaligus:
IRepository repo = new EfRepository(); // Would normally pass through IOC into constructor
var c1 = new Country() { Name = "United States", CountryCode = "US" };
var c2 = new Country() { Name = "Canada", CountryCode = "CA" };
var c3 = new Country() { Name = "Mexico", CountryCode = "MX" };
var p1 = new Province() { Country = c1, Name = "Alabama", Abbreviation = "AL" };
var p2 = new Province() { Country = c1, Name = "Alaska", Abbreviation = "AK" };
var p3 = new Province() { Country = c2, Name = "Alberta", Abbreviation = "AB" };
repo.Add<Country>(c1);
repo.Add<Country>(c2);
repo.Add<Country>(c3);
repo.Add<Province>(p1);
repo.Add<Province>(p2);
repo.Add<Province>(p3);
repo.Save();
Namun, sisa implementasi dari Repository memiliki ketergantungan yang kuat pada Linq:
IQueryable<T> Query();
IList<T> Find(Expression<Func<T,bool>> predicate);
T Get(Expression<Func<T,bool>> predicate);
T First(Expression<Func<T,bool>> predicate);
//... and so on
Pola repositori ini bekerja sangat baik untuk Entity Framework, dan cukup banyak menawarkan pemetaan metode 1 ke 1 yang tersedia di DbContext / DbSet. Tetapi mengingat penyerapan Linq yang lambat pada teknologi akses data lainnya di luar Entity Framework, apa keuntungan yang diberikan ini daripada bekerja langsung dengan DbContext?
Saya mencoba untuk menulis versi PetaPoco dari Repositori, tetapi PetaPoco tidak mendukung Linq Expressions, yang membuat membuat antarmuka IRepository generik sangat tidak berguna kecuali Anda hanya menggunakannya untuk GetAll, GetById, Add, Update, Delete, dan Save dasar metode dan menggunakannya sebagai kelas dasar. Maka Anda harus membuat repositori spesifik dengan metode khusus untuk menangani semua klausa "di mana" yang sebelumnya bisa saya sampaikan sebagai predikat.
Apakah pola Gudang Generik berguna untuk apa pun di luar Kerangka Entitas? Jika tidak, mengapa seseorang menggunakannya sama sekali alih-alih bekerja langsung dengan Entity Framework?
Tautan asli tidak mencerminkan pola yang saya gunakan dalam kode sampel saya. Ini adalah ( tautan yang diperbarui ).
Jawaban:
Repositori generik bahkan tidak berguna (dan IMHO juga buruk) untuk Entity Framework. Itu tidak membawa nilai tambahan apa pun yang sudah disediakan oleh
IDbSet<T>
(yaitu btw. Generic repository).Seperti yang telah Anda temukan argumen bahwa repositori generik dapat diganti dengan implementasi untuk teknologi akses data lainnya cukup lemah karena dapat menuntut penulisan penyedia Linq Anda sendiri.
Argumen umum kedua tentang pengujian unit yang disederhanakan juga salah karena mengejek repositori / set dengan penyimpanan data dalam memori menggantikan penyedia Linq dengan yang lain yang memiliki kemampuan berbeda. Penyedia Linq-to-entitas hanya mendukung sebagian dari fitur Linq - bahkan tidak mendukung semua metode yang tersedia pada
IQueryable<T>
antarmuka. Berbagi pohon ekspresi antara lapisan akses data dan lapisan logika bisnis mencegah pemalsuan lapisan akses data - logika kueri harus dipisahkan.Jika Anda ingin memiliki abstraksi "generik" yang kuat, Anda harus melibatkan pola-pola lain juga. Dalam hal ini Anda perlu menggunakan bahasa permintaan abstrak yang dapat diterjemahkan oleh repositori ke bahasa permintaan tertentu yang didukung oleh lapisan akses data yang digunakan. Ini ditangani oleh pola Spesifikasi. Linq on
IQueryable
adalah spesifikasi (tetapi terjemahan membutuhkan penyedia - atau pengunjung kustom yang menerjemahkan pohon ekspresi ke dalam kueri) tetapi Anda dapat menentukan versi Anda sendiri yang disederhanakan dan menggunakannya. Misalnya NHibernate menggunakan Kriteria API. Tetap cara paling sederhana adalah menggunakan repositori spesifik dengan metode tertentu. Cara ini adalah yang paling sederhana untuk diterapkan, paling sederhana untuk menguji dan paling sederhana untuk palsu dalam unit test karena logika kueri sepenuhnya tersembunyi dan dipisahkan di belakang abstraksi.sumber
ISession
yang mudah dipermainkan untuk tujuan pengujian unit umum, dan saya dengan senang hati akan melepaskan 'repositori' ketika bekerja dengan EF juga - tetapi apakah ada cara yang lebih mudah dan langsung untuk menciptakan kembali itu? Sesuatu yang miripISession
danISessionFactory
, karena tidak adaIDbContext
sejauh yang saya tahu ...IDbContext
- jika Anda mau,IDbContext
Anda bisa membuatnya dan menerapkannya pada konteks turunan Anda.Masalahnya bukan pola repositori. Memiliki abstraksi antara mendapatkan data dan bagaimana Anda mendapatkannya adalah hal yang baik.
Masalahnya di sini adalah implementasinya. Dengan asumsi bahwa ekspresi sewenang-wenang akan berfungsi untuk memfilter sangat tidak pasti.
Membuat repositori berfungsi untuk semua objek Anda secara langsung agak melenceng. Objek data akan jarang jika pernah memetakan langsung ke objek bisnis. Melewati T untuk memfilter sangat tidak masuk akal dalam situasi ini. Dan memberikan banyak fungsionalitas itu cukup banyak jaminan bahwa Anda tidak dapat mendukung semuanya begitu penyedia lain datang.
sumber
Nilai lapisan data generik (repositori adalah jenis lapisan data tertentu) memungkinkan kode untuk mengubah mekanisme penyimpanan yang mendasarinya dengan sedikit atau tanpa dampak pada kode panggilan.
Secara teori, ini bekerja dengan baik. Dalam praktiknya, seperti yang telah Anda amati, abstraksi sering bocor. Mekanisme yang digunakan untuk mengakses data dalam satu berbeda dengan mekanisme yang lain. Dalam beberapa kasus, Anda akhirnya menulis kode dua kali: sekali di lapisan bisnis dan mengulanginya di lapisan data.
Cara paling efektif untuk membuat lapisan data umum adalah dengan mengetahui berbagai jenis sumber data yang akan digunakan aplikasi sebelumnya. Seperti yang Anda lihat, dengan asumsi LINQ atau SQL bersifat universal dapat menjadi masalah. Mencoba retrofit penyimpanan data baru kemungkinan akan menghasilkan penulisan ulang.
[Sunting: Menambahkan yang berikut.]
Ini juga tergantung pada apa yang dibutuhkan aplikasi dari lapisan data. Jika aplikasi hanya memuat atau menyimpan objek, lapisan data bisa sangat sederhana. Karena kebutuhan untuk mencari, mengurutkan dan memfilter meningkat, kompleksitas lapisan data meningkat dan abstraksi mulai menjadi bocor (seperti mengekspos kueri LINQ dalam pertanyaan). Namun, setelah pengguna dapat memberikan kueri sendiri, biaya / manfaat lapisan data perlu ditimbang dengan cermat.
sumber
Memiliki lapisan kode di atas database bermanfaat dalam hampir semua kasus. Saya biasanya lebih suka pola "GetByXXXXX" dalam kode tersebut - ini memungkinkan Anda mengoptimalkan permintaan di belakangnya sesuai kebutuhan sambil menjaga UI bebas dari kekacauan antarmuka data.
Mengambil keuntungan dari obat generik jelas merupakan permainan yang adil - memiliki
Load<T>(int id)
metode membuat banyak akal. Tapi membangun repositori di sekitar LINQ adalah setara dengan 2010 dari menjatuhkan permintaan sql di mana-mana dengan sedikit keamanan jenis tambahan.sumber
Nah, dengan tautan yang disediakan saya dapat melihat bahwa itu mungkin menjadi pembungkus berguna untuk
DataServiceContext
, tetapi tidak mengurangi manipulasi kode atau meningkatkan keterbacaan. Selain itu, akses keDataServiceQuery<T>
terhambat, membatasi fleksibilitas untuk.Where()
dan.Single()
. Juga tidak adaAddRange()
atau alternatif yang disediakan. Juga tidakDelete(Predicate)
disediakan yang bisa berguna (repo.Delete<Person>( p => p.Name=="Joe" );
untuk menghapus Joe-s). DllKesimpulan: API semacam itu menghalangi API asli dan membatasinya pada beberapa operasi sederhana.
sumber
Saya akan mengatakan "Ya". Bergantung pada model data Anda, Generic Repository yang dikembangkan dengan baik dapat membuat tier akses data lebih ramping, lebih rapi, dan jauh lebih kuat.
Baca seri artikel ini (oleh @ chris-pratt ):
sumber