Genel Depoya Gerçek Bir Avantaj Var mı?


28

Yeni bir uygulama için Genel Depo oluşturmanın avantajları hakkında bazı makaleler okuyordum ( örnek ). Fikir güzel gözüküyor, çünkü aynı depoyu birkaç farklı varlık türü için aynı anda birkaç şey yapmak için kullanmama izin veriyor:

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();

Bununla birlikte, Deponun uygulanmasının geri kalanının Linq’e büyük bir dayanağı vardır:

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

Bu depo kalıbı, Entity Framework için harika çalıştı ve hemen hemen DbContext / DbSet'te bulunan yöntemlerin 1 ila 1 eşlemesini teklif etti. Ancak, Linq'in Entity Framework dışındaki diğer veri erişim teknolojilerindeki yavaş alımı göz önüne alındığında, bunun doğrudan DbContext ile çalışmanın avantajı nedir?

Deponun PetaPoco sürümünü yazmaya çalıştım , ancak PetaPoco, yalnızca temel GetAll, GetById, Ekle, Güncelle, Sil ve Kaydet için kullanmazsanız, genel bir IRepository arayüzü oluşturmayı pek işe yaramaz hale getiren Linq Expressions'ı desteklemiyor. temel sınıf olarak yöntemler ve kullanır. Öyleyse, daha önce bir yordam olarak geçirebileceğim tüm "where" cümleciklerini işlemek için özel yöntemlerle özel depolar oluşturmalısınız.

Genel Havuz modeli, Varlık Çerçevesi dışındaki herhangi bir şey için yararlı mı? Eğer değilse, neden doğrudan Entity Framework ile çalışmak yerine birileri onu kullansın ki?


Orijinal bağlantı, örnek kodumda kullandığım deseni yansıtmıyor. İşte bir ( güncellenmiş bağlantı ).



1
Soru gerçekten "genel veri havuzunun eklenmesi" ile mi ilgili, yoksa "genel veri havuzu ara yüzünün ardındaki karmaşık sorguları nasıl gizleyeceğim" mi? Arabirimi ve kullanımı linq'e bağlıysa, genel olması mümkün mü? Repositoryapi, arama tekniğinden tamamen bağımsız olan ve uygulamanın kaydırılmasına izin veren bir QueryByExample yöntemine sahiptir.
k3b

"Birkaç farklı varlık türü için birkaç şey yapmak için aynı depoyu kullanmama izin veriyor" => Bana mı bakıyorsunuz yoksa genel bir havuzun ne olduğunu yanlış mı anladınız (söz konusu makaleyle ilgili olarak)? Bana göre, jenerik depo her zaman tüm depoları tek bir sınıf ya da arayüze hükmetmek anlamına geliyordu, her ne tür olursa olsun tüm varlıkların kalıcılığını yöneten tek bir depo örneğine sahip değildi ...
guillaume31

Yanıtlar:


35

Genel veri havuzu, Entity Framework için bile yararsızdır (ve IMHO da kötüdür). Önceden sağlananlara IDbSet<T>(btw. Jenerik depo) ek bir değer getirmez .

Genel veri havuzunun diğer veri erişim teknolojisi için uygulama ile değiştirilebileceği argümanını zaten bulduğunuz gibi, kendi Linq sağlayıcınızı yazmayı talep edebileceğinden oldukça zayıftır.

Basitleştirilmiş birim sınaması hakkındaki ikinci ortak argüman da yanlıştır çünkü bellekteki veri depolaması ile veri havuzunu ayarlamak / ayarlamak, Linq sağlayıcısının yerine farklı yetenekleri olan bir tane verir. Linq-varlık sağlayıcı, yalnızca Linq özelliklerinin alt kümesini destekler; IQueryable<T>arabirimde mevcut olan tüm yöntemleri desteklemez . İfade ağaçlarının veri erişim katmanı ve iş mantığı katmanları arasında paylaşılması, veri erişim katmanının taklit edilmesini önler - sorgu mantığı ayrılmalıdır.

Güçlü bir "genel" soyutlamaya sahip olmak istiyorsanız, diğer kalıpları da dahil etmeniz gerekir. Bu durumda, kullanılan veri erişim katmanı tarafından desteklenen belirli bir sorgu diline havuz tarafından çevrilebilen soyut bir sorgu dili kullanmanız gerekir. Bu şartname modeli tarafından ele alınır. Linq on IQueryablebir özelliktir (ancak çeviri sağlayıcı gerektirir - veya ifade ağacını sorguya çeviren bazı özel ziyaretçiler) ancak kendi basitleştirilmiş sürümünüzü tanımlayabilir ve kullanabilirsiniz. Örneğin, NHibernate, Ölçüt API'sini kullanır. Yine de en basit yol, belirli yöntemlerle belirli bir depo kullanmaktır. Bu şekilde, uygulama mantığı, test edilmesi en basit ve birim testlerinde sahte olması en basit yöntemdir, çünkü sorgu mantığı tamamen gizlenir ve soyutlamanın arkasına ayrılır.


Sadece hızlı bir soru olan NHibernate, ISessiongenel birim test amaçları için kolayca takılabilir olana sahiptir ve EF ile çalışırken de 'depoyu' memnuniyetle düşürürdüm - ama bunu yeniden yaratmanın daha kolay ve kolay bir yolu var mı? Benzer bir şey ISessionve ISessionFactoryhiçbir orada, 'neden IDbContextkadarıyla söyleyebilirim ...
Patryk Cwiek

Hayır, hayır yok IDbContext- isterseniz IDbContextbir tane oluşturabilir ve türetilmiş bağlamınıza uygulayabilirsiniz.
Ladislav Mrnka

Yani, günlük MVC uygulamaları için, IDbSet <T> 'nin depo modelini tamamen unutmak için yeterince iyi bir genel depo sağlaması gerektiğini söylüyorsunuz. MVC projenizde DAL referanslarına izin verilmediği özel durumlar hariç.
ProfK

1
İyi cevap. Bazı geliştiriciler, sebepsiz yere başka bir soyutlama katmanı oluşturur. IMO, EF genel depoya veya bir çalışma birimine (yerleşik değil) ihtiyaç duymaz
ashraf

1
IDbSet <T>, Varlık Çerçevesine bağımlılığı ortadan kaldırmak için genel bir depo kullanmayı amaçlayan bir tür Varlık Çerçevesine bağımlılığı olan jenerik bir havuzdur.
Joel McBeth

7

Sorun depo deseni değil. Veri alma ile nasıl elde ettiğiniz arasında bir soyutlama yapmak iyi bir şeydir.

Buradaki sorun uygulamadır. Keyfi bir ifadenin filtreleme için işe yarayacağını varsaymak en iyisidir.

Tüm nesneleriniz için bir depo çalışması yapmak, doğrudan bu noktayı özlüyor. Veri nesneleri, doğrudan doğrudan iş nesnelerine eşlenirse nadiren olacaktır. T'yi filtrelemek için geçmek bu durumlarda çok daha az anlamlıdır. Ve bu kadar çok işlevsellik sağlamak, farklı bir sağlayıcı geldiğinde hepsini destekleyemeyeceğinizi garanti eder.


Bu nedenle, GetById (int id), SortedList (), vb. Gibi belirli yöntemlerle veri nesnesi başına bir veri havuzuna veya sıkıca bağlantılı nesnelere sahip bir havuza sahip olmak daha mantıklı olur mu? Bir veri havuzundan veri nesnelerinin listelerini mi iade ediyorum, yoksa bunları gerekli alanlarla birlikte iş nesnelerine mi dönüştürüyorum? Bunun Hizmet / İş katmanında ne olduğunu sanıyordum.
Sam

1
@ sam - Daha belirgin depoları tercih ederim, evet. Eğer çeviri yapıyorlarsa veya yapmamaları nerede bir şeylerin değişebileceğine bağlı. Etki alanınız iyi tanımlanmışsa, şeyleri etki alanı biçiminde döndürürüm. Veriler iyi tanımlanmışsa, şeyleri veri yapıları biçiminde döndürürüm. Olmazsa, inşa etmek ve daha sonra buna adapte olmak için iyi tanımlanmış bir temel olarak hizmet veren bir varlığa sahibim.
Telastyn

1
Ortak eşyaları genel bir depoya devredecek belirli havuzlara sahip olmanızın bir nedeni yoktur, ayrıca daha karmaşık çağrılar için ek depoya özgü yöntemler de vardır. Birkaç kez bu şekilde yapıldığını gördüm.
Eric King

@EricKing Bende de var ve orada iyiydi, ancak bazı şeyler soyutlama eğilimindedir, çünkü ortak şeyler yalnızca verilerin depolanma biçiminin ortaklığından dolayı var olma eğilimindedir (örneğin, GetByID, ID'li tablolar gerektirir).
Telastyn

@Telastyn Evet, buna katılıyorum, ancak belirli depolarda da oluyor. Sızdıran soyutlamalar, genel depolara özgü değildir. (Vay be, daha sakarca bir şekilde ifade edebilir miyim?)
Eric King

2

Genel bir veri katmanının değeri (bir depo, belirli bir veri katmanı türüdür), kodun, temeldeki depolama mekanizmasını, çağıran kod üzerinde çok az veya hiç etkisi olmadan değiştirmesine olanak tanır.

Teorik olarak, bu iyi çalışıyor. Uygulamada, gördüğünüz gibi, soyutlama çoğu zaman sızdırıyor. Birindeki verilere erişmek için kullanılan mekanizmalar, diğerindeki mekanizmalardan farklıdır. Bazı durumlarda, kodu iki kere yazıyorsunuz: bir kez iş katmanında ve tekrar veri katmanında tekrarlıyorsunuz.

Genel bir veri katmanı oluşturmanın en etkili yolu, uygulamanın önceden kullanacağı farklı veri kaynağı türlerini bilmektir. Gördüğünüz gibi LINQ veya SQL'in evrensel olduğunu varsaymak bir sorun olabilir. Yeni veri depolarını güçlendirmeyi denemek muhtemelen yeniden yazma ile sonuçlanacaktır.

[Düzenle: Aşağıdaki eklendi.]

Ayrıca, uygulamanın veri katmanından neye ihtiyacı olduğuna da bağlıdır. Uygulama yalnızca nesneleri yüklüyor veya saklıyorsa, veri katmanı çok basit olabilir. Arama, sıralama ve filtreleme ihtiyacı arttıkça, veri katmanının karmaşıklığı artar ve soyutlamalar sızmaya başlar (örneğin, söz konusu LINQ sorgularını açığa vurma gibi). Ancak kullanıcılar kendi sorgularını gönderebildiklerinde, veri katmanının maliyet / yararının dikkatlice tartılması gerekir.


1

Veritabanının üstünde bir kod katmanının olması hemen hemen her durumda faydalı olacaktır. Genelde, söz konusu kodda bir "GetByXXXXX" modelini tercih ederdim - UI'yi veri arabirimi dağınıklığından kurtarırken, gerektiği şekilde arkasında sorguları optimize etmenize olanak tanır.

Jenerik bilimden yararlanmak kesinlikle adil bir oyundur - bir Load<T>(int id)yönteme sahip olmak çok anlamlıdır. Ancak LINQ etrafında depo oluşturmak, 2010’da her yerde küçük bir ek güvenlik türü olan sql sorgularını bırakma eşdeğeridir.


0

Eh, sağlanan bağlantı ile bunun için kullanışlı bir sarıcı olabileceğini görebiliyorum DataServiceContext, ancak kod manipülasyonlarını azaltmıyor veya okunabilirliği artırmıyor. Ayrıca, erişimin DataServiceQuery<T>engellenmesi, esnekliğin sınırlandırılması .Where()ve .Single(). Ne de AddRange()alternatifler. Ayrıca Delete(Predicate)faydalı olabilecekleri de sağlanmıştır ( repo.Delete<Person>( p => p.Name=="Joe" );Joe'ları silmek için). Vb.

Sonuç: Böyle bir API, yerel API'yi engeller ve birkaç basit işlemle sınırlandırır.


Haklısın. Bu benim örnek kodunda bulunan deseni kullanarak makale değildi. Eve geldiğimde bağlantıyı bulmaya çalışacağım.
Sam,

Sorunun altına güncellenmiş bağlantı eklendi.
Sam

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.