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?
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?
Yanıtlar:
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 DbContext
Havuzu ile soyutlamanın bir başka avantajı da test edilebilirliktir . Eğer olabilir IRepository
arabirimi kullanır 2 uygulamaları, bir (gerçek Depo) sahip olduğu DbContext
veritabanı ve ikinci, konuşmak için FakeRepository
bellek içi nesneleri / alay verileri döndürebilir. Bu, ünitenizi IRepository
test 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
}
}
Depo şablonunu Entity Framework ile kullanmamak için en iyi neden? Varlık Çerçevesi zaten bir havuz düzeni uygular. DbContext
Sizin Hizmet Alanınızdır (Çalışma Birimi) ve her DbSet
biri 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.
DbContext
Ef6 + (: bakınız içinde msdn.microsoft.com/en-us/data/dn314429.aspx ). Daha küçük versiyonlarda bile, iterface uyguladığı için DbContext
alaycı DbSet
s ile sahte benzeri bir sınıf kullanabilirsiniz . DbSet
IDbSet
İş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.
DbContext
. Şimdi tamamen alay edebilirsiniz . Ne olursa olsun, her zaman alay edebilirsin DbSet
ve 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 DbContext
barındırmak için bir sınıftan biraz daha fazlasıdır DbSet
.
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.
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.
ISessionFactory
ve ISession
kolayca takılabilir) DbContext
, ne yazık ki ile o kadar kolay değil ...
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.
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.
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.
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!