Depo modelini neden Entity Framework ile kullanmamalıyım?


203

Bir iş görüşmesi sırasında, depo modelinin neden Entity Framework gibi ORM'lerle çalışmak için iyi bir model olmadığını açıklamam istendi. Neden bu böyle?


60
hileli bir soruydu
Omu

2
Muhtemelen, görüşme yapan kişilere, kuruluş çerçevesini gösterirken Microsoft'un depo modelini çok sık kullandığını yanıtlardım: | .
Laurent Bourgault-Roy

1
Öyleyse görüşmeci'nin iyi bir fikir olmamasının nedeni neydi?
Bob Horn,

3
İşin ilginç yanı, Google’da “depo modelini” araştırmanın çoğunlukla Varlık Çerçevesi ile ilgili sonuçları ve modelin EF ile nasıl kullanılacağını vermesidir.
Arseni Mourzenko

2
ayende blog kontrol ayende.com/blog .
Bildiklerime dayanarak

Yanıtlar:


99

Depo modelinin Entity Framework ile çalışmaması için hiçbir sebep görmüyorum. Havuz modeli, veri erişim katmanınıza koyduğunuz bir soyutlama katmanıdır. Veri erişim katmanınız, saf ADO.NET saklı yordamlarından Entity Framework'e veya bir XML dosyasına kadar herhangi bir şey olabilir.

Farklı kaynaklardan gelen verilerin bulunduğu büyük sistemlerde (veritabanı / XML / web servisi), soyutlama katmanına sahip olmak iyidir. Havuz modeli bu senaryoda iyi çalışır. Entity Framework’ün sahnelerin arkasındakileri gizlemek için yeterli soyutlama olduğuna inanmıyorum.

Entity Framework'lü Havuz modelini veri erişim katmanı yöntemim olarak kullandım ve henüz bir sorunla karşılaşmadım.

Bir DbContextHavuzu ile soyutlamanın bir başka avantajı da test edilebilirliktir . Eğer olabilir IRepositoryarabirimi kullanır 2 uygulamaları, bir (gerçek Depo) sahip olduğu DbContextveritabanı ve ikinci, konuşmak için FakeRepositorybellek içi nesneleri / alay verileri döndürebilir. Bu, ünitenizi IRepositorytest edilebilir hale getirir , böylece kullanılan diğer kod bölümleri 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
  }
}

Şimdi DI kullanarak, uygulamayı alıyorsunuz

public class SomeService
{
  IRepository repo;
  public SomeService(IRepository repo)
  {
     this.repo = repo;
  }  
  public void SomeMethod()
  {
    //use this.repo as needed
  }    
}

3
İşe yaramayacağını söylemedim, aynı zamanda EF ile depo modeliyle de çalıştım, ancak bugün BT veritabanını DataBase ile kullanmak için neden İYİ DEĞİLDİR,

2
Tamam, bu en popüler cevap olduğundan, Doğru Cevap olarak seçeceğim

65
En popüler olan ne zamandı? == doğru mu?
HDave

14
DbContext zaten bir havuzdur, depo düşük seviyeli bir soyutlama anlamına gelir. Farklı veri kaynaklarını soyutlamak istiyorsanız, bunları temsil edecek nesneler oluşturun.
Daniel Little,

7
ColacX. Sadece bunu denedik - kontrol panelindeki DBcontext - ve repo düzenine geri dönüyoruz. Repo deseni ile, Birim Testleri sürekli başarısız olan muazzam DbContext alaycılığından geçti. EF kullanımı zordu ve EF nüansları için araştırma saatleri kırılgandı ve maliyet saatlerini aldı. Şimdi deponun küçük basit alaycılarımız var. Kod daha temiz. İşin ayrılması daha açıktır. EF’in zaten bir repo modeli ve zaten test edilebilir bir model olduğu konusunda artık hemfikir değilim.
Rhyous

434

Depo şablonunu Entity Framework ile kullanmamak için en iyi neden? Varlık Çerçevesi zaten bir havuz düzeni uygular. DbContextSizin Hizmet Alanınızdır (Çalışma Birimi) ve her DbSetbiri havuzdur. Bunun üzerine başka bir katman uygulamak sadece gereksiz olmakla kalmaz, bakımı daha da zorlaştırır.

İnsanlar , kalıbın amacını anlamadan kalıpları takip eder. Depo deseni söz konusu olduğunda, amaç düşük seviyeli veri tabanı sorgulama mantığını soyutlamaktır. Aslında SQL deyimlerini kodunuza yazmanın eski günlerinde, depo kalıbı, bu SQL'i kod tabanınıza dağılmış tek tek yöntemlerden uzaklaştırmanın ve tek bir yerde yerelleştirmenin bir yoluydu. Entity Framework, NHibernate, vb. Gibi bir ORM'ye sahip olmak, bu kod soyutlamasının yerine geçiyor ve bu da kalıp ihtiyacını ortadan kaldırıyor.

Bununla birlikte, ORM'inizin üzerinde bir soyutlama oluşturmak kötü bir fikir değildir, yalnızca UOW / repostitory kadar karmaşık bir şey değildir. Verilerin Entity Framework, NHibernate veya Web API'den gelip gelmediğini bilmeden veya bakım vermeden uygulamanızın kullanabileceği bir API oluşturduğunuz bir servis deseni ile giderdim. Bu, çok daha basittir, çünkü uygulamanızın ihtiyaç duyduğu verileri döndürmek için yalnızca servis sınıfınıza yöntemler eklersiniz. Bir Yapılacaklar uygulaması yazıyorsanız, örneğin, bu hafta vadesi geçmiş ve henüz tamamlanmamış öğeleri iade etmek için bir servis çağrınız olabilir. Uygulamanızın tüm bildiği, eğer bu bilgiyi istiyorsa, bu yöntemi çağırıyor. Bu yöntemin içinde ve genel olarak hizmetinizde, Entity Framework veya başka ne kullanıyorsanız kullanın. Ardından, daha sonra ORM'leri değiştirmeye veya bilgileri bir Web API'sinden almaya karar verirseniz,

Depo modelini kullanmak için potansiyel bir argüman gibi görünebilir, ancak buradaki temel fark, bir hizmetin daha ince bir katman olduğu ve sorgulamaya devam ettiğiniz bir şey yerine, tam olarak pişirilmiş verileri döndürmeye yönelik olmasıdır. deposu.


68
Bu tek doğru cevap gibi görünüyor.
Mike Chamberlain,

10
Sen edebilirsiniz alay DbContextEf6 + (: bakınız içinde msdn.microsoft.com/en-us/data/dn314429.aspx ). Daha küçük versiyonlarda bile, iterface uyguladığı için DbContextalaycı DbSets ile sahte benzeri bir sınıf kullanabilirsiniz . DbSetIDbSet
Chris Pratt

14
@TheZenker, depo desenini tam olarak takip etmemiş olabilirsiniz. En büyük fark, iade değeridir. Depolar sorgulanabilirler, hizmetlerin numaralandırıcılar döndürmesi gerekir. Hatta bu kadar siyah ve beyaz bile değil, çünkü orada bir miktar çakışma var. Kullanım şeklinizde daha fazlası var. Bir depo, daha sonra sorguladığınız tüm nesneler kümesini döndürmeli, hizmet son veri kümesini döndürmeli ve daha fazla sorgulamayı desteklememelidir.
Chris Pratt

10
Çok bencilce gelme riski altında: yanılıyorlar. Şimdi, resmi eğitimlere gittiğinde, Microsoft EF6’dan beri gördüğüm depoları kullanarak geri çekildi. Kitaba ilişkin olarak, yazarın neden depoları kullanmayı seçtiğiyle konuşamıyorum. Konuşabileceğim şey, siperlerdeki birisinin büyük ölçekli uygulamalar geliştirmesi olarak, Entity Framework ile depo modelini kullanmanın bir bakım kabusu olduğudur. Bir avuç havuzdan daha karmaşık bir şeye geçtiğinizde, depolarınızı / çalışma ünitenizi yönetmek için aşırı miktarda zaman harcarsınız.
Chris Pratt

6
Genellikle veritabanı veya erişim yöntemi başına yalnızca bir hizmetim var. Aynı yöntem kümesinden birden fazla varlık türünü sorgulamak için genel yöntemler kullanıyorum. Bağlamımı hizmetime, sonra da hizmetimi denetleyicilerime enjekte etmek için Ninject'i kullanıyorum, böylece her şey temiz ve düzenli.
Chris Pratt

45

İşte Ayende Rahien'den bir örnek: Kıyamet çukurunda mimarlık: Depo soyutlama katmanının kötülükleri

Henüz onun sonucuna katılıp katılmadığımdan emin değilim. Bu bir catch-22 - bir yandan, EF Context'umu sorguya özel veri alma yöntemleriyle türe özgü depolara sardıysam, kodumu (tür) üniteyle test edebiliyorum, bu varlık ile neredeyse imkansız Yalnız çerçeve. Öte yandan, zengin sorgulama ve ilişkilerin anlamsal bakımını yapma yeteneğimi kaybediyorum (ancak bu özelliklere tam olarak erişebildiğimde bile, EF ya da seçebileceğim diğer herhangi bir ORM etrafında yumurta kabukları üzerinde yürüyormuş gibi hissediyorum. , IQueryable uygulamasının hangi yöntemleri destekleyebileceğini veya destekleyemeyeceğini asla bilmediğim için, bir navigasyon mülkü koleksiyonuna ekleme yapmamı bir teması veya yalnızca bir dernek olarak yorumlayıp yorumlamayacağını, tembel veya istekli yükü yükleyip yüklemeyeceğini yorumlayacağını varsayılan vb. belki bu daha iyi olabilir. Sıfır empedans nesne-ilişkisel "haritalama" mitolojik yaratığın bir parçasıdır - belki de bu yüzden Entity Framework'ün en son sürümünün adı "Magic Unicorn" idi.

Bununla birlikte, varlıklarınızı sorguya özgü veri alma yöntemleri ile almak, birim testlerinizin artık temelde beyaz kutu testleri olduğu ve bu konuda hiçbir seçeneğiniz olmadığı anlamına gelir, çünkü önceden test ettiğiniz ünitenin hangi depo yöntemine gideceğini önceden bilmeniz gerekir. alay etmek için arayın. Ayrıca, entegrasyon testleri de yazmadığınız sürece, sorguları gerçekten test etmiyorsunuz.

Bunlar karmaşık bir çözüme ihtiyaç duyan karmaşık problemlerdir. Tüm varlıklarınızın, aralarında ilişki olmayan ayrı türler olduğunu iddia ederek düzeltemezsiniz ve her birini kendi depolarına atomize eder. Iyi can ama berbat.

Güncelleme: Varlık Çerçevesi için Çaba sağlayıcısını kullanarak bazı başarılar elde ettim . Çaba, EF'yi gerçek bir veritabanında kullanacağınız gibi testlerde kullanmanıza izin veren bir bellek içi sağlayıcıdır (açık kaynak). Bu projedeki tüm testleri değiştirmeyi düşünüyorum, bu sağlayıcıyı kullanmak için çalışıyorum çünkü işleri çok daha kolaylaştırıyor gibi görünüyor. Bugüne kadar bulduğum tek çözüm, daha önce konuştuğum tüm konulara değiniyor. Tek şey, sınamalarım sırasında bellek içi veritabanını oluştururken (bunu yapmak için NMemory adlı başka bir paket kullanıyor) oluştururken hafif bir gecikme var, ancak bunu gerçek bir sorun olarak görmüyorum. Test için Effort (SQL CE'ye karşı) kullanımı hakkında konuşan bir Code Project makalesi var.


3
Birim testinden bahsetmeyen tüm mimari makaleler otomatik olarak benim için çöp kutusuna gönderilir. Depo modelinin noktalarından biri, bazı test yetenekleri kazanmaktır.
Sleeper Smith

3
EF İçeriğini (zaten bir havuz olan) sarmalamadan ünite testleri yapabilirsiniz. Etki alanı / hizmetlerinizi veritabanı sorgulamaları değil, tümleştirme testleridir.
Daniel Little,

2
EF'in test edilebilirliği sürüm 6'da büyük ölçüde gelişti DbContext. Şimdi tamamen alay edebilirsiniz . Ne olursa olsun, her zaman alay edebilirsin DbSetve bu zaten, Entity Framework'ün eti. Mülklerinizi (havuzları) tek bir yerde (iş birimi), özellikle de tüm veritabanı başlatma ve bağlantı öğelerinin istenmediği veya gerekli olmadığı bir birim test bağlamında DbContextbarındırmak için bir sınıftan biraz daha fazlasıdır DbSet.
Chris Pratt

İlgili varlık navigasyonunu kaybetmek kötüdür ve OOP'a karşıdır, ancak neyin sorgulandığı konusunda daha fazla kontrol sahibi olursunuz.
Alireza

Test noktasına kadar, EF Core, birim testini etkinleştirmek için kutudan çıkarılmış ve Sqlite sağlayıcılarıyla birlikte Bellek İçinde uzun bir yol kat etti. Kapsayıcı bir veritabanında testleri çalıştırmak için entegrasyon testine ihtiyaç duyduğunuzda kenetleyiciyi getirin.
Sudhanshu Mishra

16

Muhtemelen bunu yapmasının nedeni, bunun biraz fazla olması. Entity Framework size çok sayıda kodlama ve işlevsel avantaj sağlar, bu yüzden kullanırsınız, o zaman bunu alır ve bir depo düzenine sararsanız, bu avantajları ortadan kaldırırsanız, başka bir veri erişim katmanı kullanıyor olabilirsiniz.


"Varlık Çerçevesi size bir kodlama ve işlevsellik avantajı sağlar" için bazı avantajlardan bahseder misiniz?
ManirajSS

2
Demek istediği bu. var id = Entity.Where (i => i.Id == 1337) .Single () bir kapsül içine alın ve sarın, temelde dışardan bu gibi bir sorgu mantığı yapamazsınız, ya sizi A'ya daha fazla kod eklemeye zorlar depo ve kimliği alma arabirimi. B varlık mantığını depodan döndürür, böylece sorgu mantığını yazabilirsiniz (bu sadece saçma)
ColacX

14

Teoride, daha kolay tekrar kullanılabilir hale getirmek için veritabanı bağlantı mantığını enkapsüle etmenin mantıklı olduğunu düşünüyorum, ancak aşağıdaki bağlantının iddia ettiği gibi, modern çerçevelerimiz esasen bununla ilgilenmektedir.

Havuz Modelini Yeniden Düşünmek


Makaleyi beğendim, ancak IMHO kurumsal uygulamalar için, DAL ve Bl MUST. Ama bağlantıyı paylaştığınız için teşekkür ederiz

1
Şahsen ben NHibernate için doğru olduğunu düşünüyorum ( ISessionFactoryve ISessionkolayca takılabilir) DbContext, ne yazık ki ile o kadar kolay değil ...
Patryk wiwiek

6

Depo şablonunu kullanmanın çok iyi bir nedeni , iş mantığınızın ve / veya kullanıcı arayüzünüzün System.Data.Entity'den ayrılmasını sağlamaktır. Bunun, Fakes veya Mocks kullanmasına izin vererek ünite testinde gerçek faydaları da içeren sayısız avantajı vardır.


Bu cevaba katılıyorum. Depolarım, basitçe ifade ağaçları oluşturmaktan başka hiçbir şey yapmayan uzatma yöntemleridir. Doğrudan doğrudan dbcontext'in üstünde genel işlevsellik sağlayan ÇOK basit bir soyutlama. Soyutlamanın tek gerçek amacı IoC'yi biraz daha kolay hale getirmek. Bence insanlar depolarında yapmaması gereken şeyleri yapmaya çalışıyorlar. Varlık başına repo yaparlar ya da hizmet katmanında olması gereken iş mantığını koyarlar. Sadece tek bir basit genel depoya ihtiyacınız var. Gerekli değil, sadece tutarlı bir arayüz sağlar.
Brandon

Eklemek istediğim bir şey daha var. Evet CQRS, MOST vakalarında oldukça üstün bir metodolojidir. Veri tabanı çalışanları geliştiricilerle iyi çalışmadığında çalıştığım bazı müşteriler için (özellikle bankalarda birden fazla olur), SQL üzerinden EF en iyi seçenektir. Bu özel senaryoda, veritabanınız üzerinde hiçbir kontrolünüz olmadığında, depo deseni mantıklı gelir. Çünkü veri yapısını yakından andırıyor ve veritabanına ne olup bittiğini çevirmek kolaydır. Bu bence gerçekten politik ve lojistik bir karar. DB tanrıları yatıştırmak için.
Brandon

1
Aslında bu konuda daha önceki görüşlerimi sorgulamaya başlıyorum. EF, birleşik Çalışma Birimi ve Depo modelidir. Yukarıda yukarıda belirtilen EF6 ile belirtilen Chris Pratt ile Context ve DbSet nesneleriyle kolayca alay edebilirsiniz. İş mantığı sınıflarını fiili veri erişim mekanizmasından korumak için veri erişiminin sınıflara sarılması gerektiğine inanıyorum, ancak bütün domuz ve EF'i başka bir depoya sarmak ve İş Birimi soyutlamasının fazlaca gözükmesi gibi görünüyor.
James Culshaw

Bunun iyi bir cevap olduğunu sanmıyorum, çünkü destekleyici ifadeniz sadece bir tane listelerken sayısız avantaj olduğunu gösteriyor. Yaptığınız bir liste, varlık yapmak için hafıza içi bir veritabanı kullanabildiğiniz için iyi bir neden değil. birim testi.
Joel McBeth

@jcmcbeth Doğrudan benim yorumlarınıza bakarsanız, depo deseni ve EF ile ilgili orijinal fikrimi değiştirdiğimi göreceksiniz.
James Culshaw

0

Her biri kendi başına yeni IDBSet (örneğin DBContext'ten kendi IDbSet'lerini çağıran bir UserRepository ve GroupRepository örneği) oluşturan bir IoC kabı olduğunda, yinelenen ancak farklı Entity Framework DbContext örnekleri ile ilgili sorunlarımız oldu (bir MVC / web bağlamında).

Hala çalışır durumda, ancak bunun üzerine bir servis katmanı eklediğinizde ve bu hizmetler bir bağlamla oluşturulan nesnelerin başka bir bağlamda yeni bir nesneye alt koleksiyonlar olarak doğru şekilde ekleneceğini varsayar, bazen başarısız olur ve bazen ' t Taahhütlerin hızına bağlı olarak.


Bu problemle birkaç farklı projede karşılaştım.
ColacX

0

Küçük projelerde depo modelini denedikten sonra kullanmamalarını şiddetle tavsiye ederim; sisteminizi zorlaştırdığı için değil, alaycı verilerin kabus olduğu için değil, testinizin faydasız hale gelmesinden dolayı !!

Verileri alay etmek, başlıklar olmadan ayrıntı eklemenize, veritabanı sınırlarını ihlal eden kayıtlar eklemenize ve veritabanının kaldırmayı reddedeceği varlıkları kaldırmanıza olanak tanır. Gerçek dünyada, tek bir güncelleme birden fazla tabloyu, kaydı, geçmişi, özeti vb. Etkileyebilir, ayrıca en son değiştirilen tarih alanı, otomatik oluşturulmuş anahtarlar, hesaplanan alanlar gibi sütunları etkileyebilir.

Kısacası testinizi gerçek veritabanı üzerinde çalıştırmak size gerçek sonuçlar verir ve sadece hizmetlerinizi ve arayüzlerinizi değil aynı zamanda veritabanı davranışını test edebilirsiniz. Saklı yordamlarınızın verilerle doğru olanı yapıp yapmadığını, beklenen sonucu döndürdüğünü veya silmek için gönderdiğiniz kaydın gerçekten silinip silinmediğini kontrol edebilirsiniz! Bu tür testler ayrıca saklı yordamdan hatalar çıkarmayı unutmak ve binlerce senaryo gibi sorunları da ortaya çıkarabilir.

Varlık çerçevesinin şu ana kadar okuduğum makalelerin hepsinden daha iyi depo modelini uyguladığını ve başarmaya çalıştıklarının çok ötesine geçtiğini düşünüyorum.

Depo, XBase, AdoX ve Ado.Net'i kullandığımız günlerde en iyi uygulama oldu, ancak varlık !! (Depo üzerindeki havuz)

Son olarak, çok fazla insanın depo modelini öğrenmek ve uygulamak için çok fazla zaman harcadığını düşünüyorum ve gitmesine izin vermeyi reddediyorlar. Çoğunlukla kendilerine zamanlarını boşa harcamadıklarını kanıtlamak için.


1
Bunun haricinde, veritabanı davranışınızı ünite testlerinde test etmek istemezsiniz, çünkü tamamen bu test seviyesi değildir.
Mariusz Jamro

Evet, burada bahsettiğiniz şey entegrasyon testi ve gerçekten değerli, ancak birim testleri tamamen farklı. Birim testleriniz asla gerçek bir veritabanına çarpmamalıdır, ancak bunu yapan entegrasyon testi eklemeniz önerilir.
Chris Pratt

-3

Geçişler nedeniyle: Bağlantı dizesi web.config'te olduğu için geçişlerin çalışması mümkün değildir. Ancak, DbContext Havuz katmanında bulunur. IDbContextFactory'nin veritabanına yapılandırma dizgisi olması gerekir.

Etrafta iş var ama bunun için henüz temiz bir çözüm bulamadım!

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.