Kami sedang mengembangkan aplikasi ASP.NET MVC, dan sekarang sedang membangun repositori / kelas layanan. Saya bertanya-tanya apakah ada keuntungan utama untuk membuat antarmuka IRepository generik yang diterapkan semua repositori, dibandingkan setiap Repositori yang memiliki antarmuka unik dan serangkaian metode sendiri.
Sebagai contoh: antarmuka IRepository generik mungkin terlihat seperti (diambil dari jawaban ini ):
public interface IRepository : IDisposable
{
T[] GetAll<T>();
T[] GetAll<T>(Expression<Func<T, bool>> filter);
T GetSingle<T>(Expression<Func<T, bool>> filter);
T GetSingle<T>(Expression<Func<T, bool>> filter, List<Expression<Func<T, object>>> subSelectors);
void Delete<T>(T entity);
void Add<T>(T entity);
int SaveChanges();
DbTransaction BeginTransaction();
}
Setiap Repositori akan mengimplementasikan antarmuka ini, misalnya:
- Gudang Pelanggan: Gudang IR
- ProductRepository: IRepository
- dll.
Alternatif yang telah kami ikuti dalam proyek sebelumnya adalah:
public interface IInvoiceRepository : IDisposable
{
EntityCollection<InvoiceEntity> GetAllInvoices(int accountId);
EntityCollection<InvoiceEntity> GetAllInvoices(DateTime theDate);
InvoiceEntity GetSingleInvoice(int id, bool doFetchRelated);
InvoiceEntity GetSingleInvoice(DateTime invoiceDate, int accountId); //unique
InvoiceEntity CreateInvoice();
InvoiceLineEntity CreateInvoiceLine();
void SaveChanges(InvoiceEntity); //handles inserts or updates
void DeleteInvoice(InvoiceEntity);
void DeleteInvoiceLine(InvoiceLineEntity);
}
Dalam kasus kedua, ekspresi (LINQ atau lainnya) akan sepenuhnya terkandung dalam implementasi Repositori, siapa pun yang mengimplementasikan layanan hanya perlu tahu fungsi repositori mana yang harus dipanggil.
Saya kira saya tidak melihat keuntungan dari menulis semua sintaks ekspresi di kelas layanan dan meneruskan ke repositori. Bukankah ini berarti kode LINQ yang mudah diacak digandakan dalam banyak kasus?
Misalnya, dalam sistem faktur lama kami, kami menelepon
InvoiceRepository.GetSingleInvoice(DateTime invoiceDate, int accountId)
dari beberapa layanan yang berbeda (Pelanggan, Faktur, Akun, dll). Tampaknya jauh lebih bersih daripada menulis yang berikut di banyak tempat:
rep.GetSingle(x => x.AccountId = someId && x.InvoiceDate = someDate.Date);
Satu-satunya kelemahan saya melihat untuk menggunakan pendekatan spesifik adalah bahwa kita bisa berakhir dengan banyak permutasi fungsi Get *, tetapi ini tampaknya masih lebih baik untuk mendorong logika ekspresi ke dalam kelas-kelas Layanan.
Apa yang saya lewatkan?
sumber
Jawaban:
Ini adalah masalah setua pola Repositori itu sendiri. Pengenalan terbaru LINQ
IQueryable
, sebuah representasi seragam dari suatu query, telah menyebabkan banyak diskusi tentang topik ini.Saya lebih suka repositori spesifik sendiri, setelah bekerja sangat keras untuk membangun kerangka repositori generik. Tidak peduli apa pun mekanisme pintar yang saya coba, saya selalu berakhir pada masalah yang sama: repositori adalah bagian dari domain yang dimodelkan, dan domain itu tidak generik. Tidak setiap entitas dapat dihapus, tidak setiap entitas dapat ditambahkan, tidak setiap entitas memiliki repositori. Pertanyaan sangat bervariasi; API repositori menjadi seunik entitas itu sendiri.
Pola yang sering saya gunakan adalah memiliki antarmuka repositori tertentu, tetapi kelas dasar untuk implementasinya. Misalnya, menggunakan LINQ ke SQL, Anda bisa melakukan:
Ganti
DataContext
dengan unit kerja pilihan Anda. Contoh implementasi mungkin:Perhatikan API publik dari repositori tidak memungkinkan pengguna untuk dihapus. Selain itu, mengekspos
IQueryable
adalah kaleng cacing lainnya - ada banyak pendapat seperti pusar pada topik itu.sumber
Saya sebenarnya sedikit tidak setuju dengan jabatan Bryan. Saya pikir dia benar, yang pada akhirnya semuanya sangat unik dan sebagainya. Tetapi pada saat yang sama, sebagian besar keluar saat Anda mendesain, dan saya menemukan bahwa mendapatkan repositori generik dan menggunakannya saat mengembangkan model saya, saya bisa mendapatkan aplikasi dengan sangat cepat, kemudian refactor ke spesifisitas yang lebih besar ketika saya menemukan perlu melakukannya.
Jadi, dalam kasus seperti itu, saya sering membuat IRepository generik yang memiliki tumpukan CRUD penuh, dan itu memungkinkan saya cepat bermain dengan API dan membiarkan orang bermain dengan UI dan melakukan pengujian integrasi & penerimaan pengguna secara paralel. Kemudian, ketika saya menemukan saya memerlukan pertanyaan spesifik pada repo, dll, saya mulai mengganti ketergantungan itu dengan yang spesifik jika diperlukan dan pergi dari sana. Satu imp yang mendasarinya. mudah untuk dibuat dan digunakan (dan mungkin menghubungkan ke db di memori atau objek statis atau objek mengejek atau apa pun).
Yang mengatakan, apa yang saya mulai lakukan akhir-akhir ini adalah memecah perilaku. Jadi, jika Anda melakukan antarmuka untuk IDataFetcher, IDataUpdater, IDataInserter, dan IDataDeleter (misalnya), Anda dapat mencampur dan mencocokkan untuk menentukan persyaratan Anda melalui antarmuka dan kemudian memiliki implementasi yang menangani beberapa atau semuanya, dan saya bisa masih menyuntikkan penerapan do-it-all untuk digunakan saat saya membangun aplikasi.
paul
sumber
GetById()
,. Apakah saya harus menggunakanIRepository<T, TId>
,GetById(object id)
atau membuat asumsi dan menggunakanGetById(int id)
? Bagaimana cara kerja kunci komposit? Saya bertanya-tanya apakah seleksi generik dengan ID adalah abstraksi yang bermanfaat. Jika tidak, apa lagi yang repositori generik akan dipaksa untuk diungkapkan secara akwardly? Itu adalah garis alasan di balik abstrak implementasi , bukan antarmuka .Saya lebih suka repositori spesifik yang berasal dari repositori generik (atau daftar repositori generik untuk menentukan perilaku yang tepat) dengan tanda tangan metode yang dapat ditimpa.
sumber
Memiliki repositori generik yang dibungkus oleh repositori tertentu. Dengan begitu Anda dapat mengontrol antarmuka publik tetapi masih memiliki keuntungan dari penggunaan kembali kode yang berasal dari memiliki repositori generik.
sumber
UserRepository kelas publik: Repository, IUserRepository
Tidakkah Anda harus menyuntikkan IUserRepository untuk menghindari mengekspos antarmuka. Seperti yang dikatakan orang, Anda mungkin tidak perlu tumpukan CRUD penuh dll.
sumber