Depo kalıbı nasıl doğru kullanılır?


86

Depolarımı nasıl gruplandırmalıyım merak ediyorum? Asp.net mvc'de gördüğüm örneklerde olduğu gibi ve kitaplarımda temelde veritabanı tablosu başına bir depo kullanıyorlar. Ancak bu, daha sonra alay etmek ve benzeri şeyler için birçok depoyu aramak zorunda kalmanıza yol açan birçok depo gibi görünüyor.

Bu yüzden onları gruplandırmam gerektiğini tahmin ediyorum. Ancak onları nasıl gruplandıracağımdan emin değilim.

Şu anda tüm kayıt işlerimi idare etmek için bir kayıt Havuzu oluşturdum. Ancak güncellemem gereken 4 tablo var ve bunu yapmak için 3 depomuz vardı.

Örneğin tablolardan biri bir lisans tablosudur. Kayıt olduklarında anahtarlarına bakar ve veritabanında olup olmadığını kontrol ederim. Şimdi bu lisans anahtarını veya bu tablodan kayıttan başka bir yerde başka bir şeyi kontrol etmem gerekirse ne olur?

Bir nokta giriş olabilir (anahtarın süresinin dolup dolmadığını kontrol edin).

Peki bu durumda ne yapacağım? Kodu yeniden yazmak (DRY'yi kırmak)? Bu 2 depoyu bir araya getirmeye çalışın ve yöntemlerden hiçbirinin başka bir zamanda gerekli olmayacağını umun (örneğin, userName kullanılıp kullanılmadığını kontrol eden bir yöntemim olabilir - belki buna başka bir yerde ihtiyacım olabilir).

Ayrıca, eğer onları bir araya getirirsem, aynı depoya giden 2 hizmet katmanına ihtiyacım olur, çünkü bir sitenin 2 farklı bölümü için tüm mantığa sahip olmanın uzun olacağını ve ValidateLogin (), ValdiateRegistrationForm () gibi isimlere sahip olmam gerektiğini düşünüyorum. , ValdiateLoginRetrievePassword () vb.

Ya da Depoyu yine de arayın ve etrafta kulağa tuhaf gelen bir isim mi var?

Yeterince genel bir ada sahip bir havuz yapmak zor görünüyor, böylece uygulamanızın birçok noktası için kullanabilirsiniz ve yine de mantıklı ve bir depodaki başka bir depoyu çağırmanın iyi bir uygulama olacağını düşünmüyorum?


11
+1. Harika soru.
griegs

Bir süredir beni rahatsız ettiği için teşekkürler. Kitapta gördüklerimi çok basit bulduğum için, bu durumlarda ne yapacağınızı göstermiyorlar.
chobo2

Temelde sizinle aynı şeyi yapıyorum ve evet, eğer 1'den fazla depoda kullanılan bir linq2sql sınıfım varsa ve DRY'yi kırdığım tablo yapısını değiştirmem gerekiyorsa. İdealden daha az. Şimdi biraz daha iyi planlıyorum, bu yüzden bir linq2sql sınıfını bir kereden fazla kullanmama gerek yok ki bu endişeleri ayırmak için iyi bir şey ama bunun benim için gerçek bir sorun olacağını öngörüyorum.
griegs

Burada benzer bir soru sordum (ama aynı değil): stackoverflow.com/questions/910156/…
Grokys

Ya ama her durum için planlamak zor görünüyor. Dediğim gibi, belki giriş ve kaydı bir Kimlik Doğrulama ile birleştirebilirim ve 2 ayrı katmana sahip olabilirim. Bu muhtemelen sorunumu çözecektir. Ancak, sitemin profil sayfasında, onlara anahtarlarını herhangi bir nedenle göstermek istersem ne olur (belki htem'in değiştirmesine izin veririm veya başka bir şey). Şimdi DRY'yi kırıp aynı şeyi yazacağım ne olacak? Veya bu 3 tablonun hepsine bir şekilde iyi bir isimle sığabilecek bir depo yapmayı deneyin.
chobo2

Yanıtlar:


41

Depo modeliyle oynadığımda yanlış yaptığım bir şey - tıpkı sizin gibi, tablonun depo 1: 1 ile ilgili olduğunu düşündüm. Etki Alanı Odaklı Tasarımdan bazı kuralları uyguladığımızda - havuzları gruplama sorunu genellikle ortadan kalkar.

Depo, tablo değil, Toplama kökü başına olmalıdır . Bu, - eğer varlık yalnız yaşamamalıysa (yani - Registrantözellikle katılan Registrationbir varlığa sahipseniz ) - sadece bir varlıktır, bir depoya ihtiyaç duymaz, toplu kök deposu aracılığıyla güncellenmeli / oluşturulmalı / alınmalıdır. aittir.

Elbette - çoğu durumda, bu depo sayısını azaltma tekniği (aslında - daha çok alan modelinizi yapılandırmak için bir tekniktir) uygulanamaz çünkü her varlığın bir toplu kök olması gerekir (bu büyük ölçüde alan adınıza bağlıdır, Yalnızca kör tahminler sağlayabilirim). Örneğinizde - Licensetoplu bir kök gibi görünüyor çünkü onları Registrationvarlık bağlamı olmadan kontrol edebilmeniz gerekiyor .

Ancak bu bizi depoları basamaklamakla sınırlamaz ( Registrationhavuz, Licensegerekirse depoya başvurabilir). Bu bizi Licensedoğrudan Registrationnesneden veri havuzuna (tercihen - IoC aracılığıyla) başvurmakla kısıtlamaz .

Tasarımınızı teknolojilerin sağladığı karmaşıklıklardan veya bir şeyi yanlış anlamadan yönlendirmemeye çalışın. Depoları ServiceXsırf 2 depo inşa etmek istemediğiniz için gruplamak iyi bir fikir değildir.

Buna uygun bir ad vermek çok daha iyi olur - RegistrationServiceyani

Ancak genel olarak hizmetlerden kaçınılmalıdır - bunlar genellikle anemik alan modeline yol açan nedenlerdir .

DÜZENLEME:
IoC'yi kullanmaya başlayın. Bağımlılık enjekte etmenin acısını gerçekten hafifletir.
Yazmak yerine:

var registrationService = new RegistrationService(new RegistrationRepository(),  
      new LicenseRepository(), new GodOnlyKnowsWhatElseThatServiceNeeds());

yazabileceksiniz:

var registrationService = IoC.Resolve<IRegistrationService>();

Ps Sözde ortak servis bulucuyu kullanmak daha iyi olurdu ama bu sadece bir örnek.


17
Hahaha ... Servis bulucuyu kullanmak için tavsiye veriyorum. Geçmişte ne kadar aptal olduğumu görmek her zaman seviniyor.
Arnis Lapsa

1
@Arnis Görünüşe göre fikirleriniz değişmiş gibi - İlgileniyorum şimdi bu soruyu nasıl farklı cevaplarsınız?
ngm

10
@ngm bunu cevapladığımdan beri çok şey değişti. Hala toplu kökün işlem sınırlarını çizmesi (bir bütün olarak kaydedilmesi) gerektiğini kabul ediyorum, ancak depo modelini kullanarak kalıcılığı soyutlama konusunda çok daha az iyimserim. Son zamanlarda - ORM'yi doğrudan kullanıyorum çünkü istekli / tembel yükleme yönetimi gibi şeyler çok garip. Kalıcılığı soyutlamaya odaklanmak yerine zengin alan modeli geliştirmeye odaklanmak çok daha faydalıdır.
Arnis Lapsa

2
@ Geliştirici hayır, tam olarak değil. yine de ısrarcı cahil olmalıdırlar. dışarıdan toplu kökü alırsınız ve işi yapan yöntemi çağırırsınız. alan modelimde sıfır referans var, sadece standart. Bunu başarmak için, zengin alan modeline ve yeterince akıllı araçlara sahip olmanız gerekir (NHibernate hile yapar).
Arnis Lapsa

1
Aksine. Birden fazla ggregate almak, genellikle PK veya dizinleri kullanabileceğiniz anlamına gelir. EF'in ürettiği ilişkilerden yararlanmaktan çok daha hızlı. Daha küçük kök kümeleri, bunlardan daha kolay performans elde eder.
jgauffin

5

Bunu ele almak için yapmaya başladığım bir şey, aslında N depoları saran hizmetler geliştirmektir. Umarım DI veya IoC çerçeveleriniz bunu kolaylaştırmaya yardımcı olabilir.

public class ServiceImpl {
    public ServiceImpl(IRepo1 repo1, IRepo2 repo2...) { }
}

bu mantıklı mı? Ayrıca, bu malikanedeki hizmetlerden bahsetmenin DDD ilkelerine uyup uymayabileceğini anlıyorum, sadece işe yarıyor gibi göründüğü için yapıyorum.


1
Hayır, bu pek mantıklı değil. Şu anda DI veya IoC çerçevelerini kullanmıyorum çünkü olduğu gibi tabağımda yeterince var.
chobo2

1
Depolarınızı yalnızca yeni bir () ile somutlaştırabilirseniz, şunu deneyebilirsiniz ... public ServiceImpl (): bu (yeni Repo1, yeni Repo2 ...) {} hizmette ek bir kurucu olarak.
neouser99

Bağımlılık enjeksiyonu mu? Bunu zaten yapıyorum ama kodunuzun ne olduğundan ve neyi çözdüğünden hala emin değilim.
chobo2

Özellikle depoların birleştirilmesinin peşinden gidiyorum. Hangi kod mantıklı değil? DI kullanıyorsanız, cevaptaki kod, DI çerçevenizin bu IRepo hizmetlerini enjekte edeceği anlamında çalışacaktır, yorum kodunda temelde DI yapmak için küçük bir çalışma vardır (temelde parametresiz yapıcınız 'enjeksiyonlar') bu bağımlılıklar ServiceImpl'nize).
neouser99

Zaten DI kullanıyorum, böylece daha iyi birim testi yapabilirim. Benim sorunum şu ki, servis katmanlarınızı ve depolarınızı ayrıntılı isimlerle yaparsanız. Sonra onları başka bir yerde kullanmanız gerekirse, ProfileClass gibi söylenen başka bir sınıfta RegRepo'yu Çağıran RegistrationService Katmanı gibi arama garip görünecektir. Yani sizden tam olarak ne yaptığınızı bir örnek göremiyorum. Gibi Aynı servis katmanında çok fazla Repoya sahip olmaya başlarsanız, çok farklı iş mantığına ve doğrulama mantığına sahip olursunuz. Servis katmanından beri genellikle doğrulama mantığını koyarsınız. O kadar çok daha fazlasına ihtiyacım var ...
chobo2

2

Yaptığım şey şu şekilde tanımlanan soyut bir temel sınıfım var:

public abstract class ReadOnlyRepository<T,V>
{
     V Find(T lookupKey);
}

public abstract class InsertRepository<T>
{
     void Add(T entityToSave);
}

public abstract class UpdateRepository<T,V>
{
     V Update(T entityToUpdate);
}

public abstract class DeleteRepository<T>
{
     void Delete(T entityToDelete);
}

Daha sonra, soyut temel sınıftan deponuzu türetebilir ve örneğin genel argümanlar farklı olduğu sürece tek deponuzu genişletebilirsiniz;

public class RegistrationRepository: ReadOnlyRepository<int, IRegistrationItem>,
                                     ReadOnlyRepository<string, IRegistrationItem> 

vb....

Ayrı depolara ihtiyacım var çünkü bazı depolarımızda kısıtlamalarımız var ve bu bize maksimum esneklik sağlıyor. Bu yardımcı olur umarım.


Yani tüm bunlarla başa çıkmak için genel bir depo mu yapmaya çalışıyorsun?
chobo2

Öyleyse, gerçekte ne söylenir Güncelleme yöntemi. Bu V güncellemesini beğendiğiniz ve bir T entitytoUpdate'e geçtiği gibi, ancak gerçekten onu güncelleyen bir kod yok veya var mı?
chobo2

Evet .. Güncelleme yönteminde kod olacak, çünkü genel depodan inen bir sınıf yazacaksınız. Uygulama kodu Linq2SQL veya ADO.NET olabilir veya veri erişim uygulama teknolojiniz olarak seçmiş olduğunuz herhangi bir şey olabilir
Michael Mann

2

Bunu depo sınıfım olarak aldım ve evet tablo / alan deposunda genişletiyorum ama yine de bazen DRY'yi kırmam gerekiyor.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MvcRepository
{
    public class Repository<T> : IRepository<T> where T : class
    {
        protected System.Data.Linq.DataContext _dataContextFactory;

        public IQueryable<T> All()
        {
            return GetTable.AsQueryable();
        }

        public IQueryable<T> FindAll(Func<T, bool> exp)
        {
            return GetTable.Where<T>(exp).AsQueryable();
        }

        public T Single(Func<T, bool> exp)
        {
            return GetTable.Single(exp);
        }

        public virtual void MarkForDeletion(T entity)
        {
            _dataContextFactory.GetTable<T>().DeleteOnSubmit(entity);
        }

        public virtual T CreateInstance()
        {
            T entity = Activator.CreateInstance<T>();
            GetTable.InsertOnSubmit(entity);
            return entity;
        }

        public void SaveAll()
        {
            _dataContextFactory.SubmitChanges();
        }

        public Repository(System.Data.Linq.DataContext dataContextFactory)
        {
            _dataContextFactory = dataContextFactory;
        }

        public System.Data.Linq.Table<T> GetTable
        {
            get { return _dataContextFactory.GetTable<T>(); }
        }

    }
}

DÜZENLE

public class AdminRepository<T> : Repository<T> where T: class
{
    static AdminDataContext dc = new AdminDataContext(System.Configuration.ConfigurationManager.ConnectionStrings["MY_ConnectionString"].ConnectionString);

    public AdminRepository()
        : base( dc )
    {
    }

Ayrıca Linq2SQL.dbml sınıfı kullanılarak oluşturulmuş bir veri bağlamım var.

Artık Hepsi ve Bul gibi standart çağrıları uygulayan standart bir depom var ve Yönetici Depomda belirli çağrılarım var.

KURU sorusunun cevabını sanmıyorum da.


Ne için "Depo"? ve CreateInstance gibi mi? Sahip olduğunuz her şeyin ne yaptığından emin değilim.
chobo2

Her şey kadar geneldir. Temel olarak (alanınız) için belirli bir depoya sahip olmanız gerekir. AdminRepository için yukarıdaki düzenlemeye göz atın.
griegs


1

Depo kalıbı kötü bir tasarım kalıbıdır. Birçok eski .Net projesiyle çalışıyorum ve bu model genellikle önlenebilecek "Dağıtılmış İşlemler", "Kısmi Geri Alma" ve "Bağlantı Havuzu Tükendi" hatalarına neden oluyor. Sorun, modelin bağlantıları ve işlemleri dahili olarak ele almaya çalışması, ancak bunların Denetleyici katmanında ele alınması gerektiğidir. Ayrıca EntityFramework zaten mantığın çoğunu özetliyor. Paylaşılan kodu yeniden kullanmak yerine Hizmet modelini kullanmanızı öneririm.


0

Sharp Mimarlık'a bakmanızı öneririm . Varlık başına bir depo kullanılmasını önerirler. Şu anda projemde kullanıyorum ve sonuçlardan memnun kaldım.


Burada +1 verirdim, ancak DI veya IoC konteynerlerinin pek bir seçenek olmadığını belirtti (bunların Sharp Arch'ın tek faydası olmadığı kabul edildi). Sanırım etrafında çalıştığı birçok mevcut kodun bir kısmı var.
neouser99

Bir varlık olarak kabul edilen nedir? Veritabanının tamamı bu mu? yoksa bu bir veritabanı tablosu mu? DI veya IoC konteynerleri şu anda bir seçenek değil çünkü aynı anda öğrendiğim diğer 10 şeyden fazlasını öğrenmek istemiyorum. sitemin bir sonraki revizyonuna veya bir sonraki projeme bakacağım şeyler. Bunun siteye hızlıca bakıp bakmayacağından emin olmasam da ve nhirbrate kullanmanızı istiyor gibi görünüyor ve şu anda linq to sql için kullanıyorum.
chobo2
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.