Ioc / DI - Neden uygulamanın giriş noktasındaki tüm katmanlara / derlemelere başvurmam gerekiyor?


123

(Bu soruyla ilgili olarak, EF4: Geç yükleme etkinleştirildiğinde neden proxy oluşturma etkinleştirilmelidir? ).

DI'da yeniyim, bu yüzden bana katlanın. Konteynırın tüm kayıtlı türlerimın örneğini oluşturmaktan sorumlu olduğunu anlıyorum, ancak bunu yapmak için çözümümdeki tüm DLL'lere ve bunların referanslarına bir başvuru gerektiriyor.

Bir DI kapsayıcı kullanmasaydım, MVC3 uygulamamdaki EntityFramework kitaplığına başvurmak zorunda kalmazdım, yalnızca DAL / Repo katmanıma başvuran iş katmanıma başvurmak zorunda kalmazdım.

Günün sonunda tüm DLL'lerin bin klasörüne dahil edildiğini biliyorum, ancak benim sorunum gerekli tüm dosyalarla bir WAP yayınlayabilmek için VS'de "referans ekle" yoluyla buna açıkça başvurmak zorunda kalıyor.


1
.NET'te Bağımlılık Enjeksiyonu kitabından alınan bu alıntı , ikinci baskı hem Mark'ın hem de benim yanıtlarının daha ayrıntılı bir versiyonudur. Kompozisyon Kökü kavramını ayrıntılı olarak açıklar ve uygulamanın başlangıç ​​yolunun diğer tüm modüle bağlı olmasına izin vermenin aslında iyi bir şey olduğunu açıklar .
Steven

Bu alıntı bağlantısını ve 1. bölümü okudum, DI'nin karmaşık meselesine benzetmelerden ve basit açıklamalardan gerçekten keyif aldığım için kitabı satın alacağım. Bence yeni bir cevap önermelisiniz, net bir şekilde cevap vermelisiniz: "Aynı zamanda kompozisyon kökünüz değilse, giriş mantıksal katmanındaki tüm katmanlara / derlemelere başvurmanız gerekmiyor", alıntıya bağlantı verin ve Şekil 3'teki resmi alıntı.
diegohb

Yanıtlar:


194

Bir DI kapsayıcısı kullanmasaydım, MVC3 uygulamamdaki EntityFramework kitaplığına başvurmak zorunda kalmazdım, yalnızca DAL / Repo katmanıma başvuran iş katmanıma.

Evet, DI'nin kaçınmak için çok çalıştığı durum tam olarak bu :)

Sıkıca bağlanmış kodla, her kitaplığın yalnızca birkaç referansı olabilir, ancak bunların yine başka referansları vardır ve aşağıdaki gibi derin bir bağımlılık grafiği oluşturur:

Derin Grafik

Bağımlılık grafiği derin olduğu için, çoğu kütüphanenin diğer birçok bağımlılık boyunca sürüklendiği anlamına gelir - örneğin diyagramda, Kitaplık C Kitaplık H, Kitaplık E, Kitaplık J, Kitaplık M, Kitaplık K ve Kitaplık N boyunca sürükler . Bu, her kitaplığı diğerlerinden bağımsız olarak yeniden kullanmayı zorlaştırır - örneğin birim testinde .

Bununla birlikte, gevşek bağlı bir uygulamada, tüm referansları Bileşim Kökü'ne taşıyarak , bağımlılık grafiği ciddi şekilde düzleştirilir :

Sığ Grafik

Yeşil renkte gösterildiği gibi, artık istenmeyen bağımlılıkları sürüklemeden Kitaplık C'yi yeniden kullanmak mümkün .

Ancak, birçok DI Konteynerler ile, bütün bu dedi, sen yok olması gereken tüm kütüphanelere sert başvurular ekleyin. Bunun yerine, konvansiyona dayalı montaj taraması (tercih edilen) veya XML konfigürasyonu şeklinde geç bağlamayı kullanabilirsiniz .

Ancak bunu yaptığınızda, derlemeleri uygulamanın bin klasörüne kopyalamayı unutmamalısınız, çünkü bu artık otomatik olarak gerçekleşmez. Şahsen, nadiren bu ekstra çabaya değer buluyorum.

Bu cevabın daha ayrıntılı bir versiyonu Dependency Injection, Principles, Practices, Patterns kitabımdan bu alıntıda bulunabilir .


3
Çok teşekkürler, bu artık çok mantıklı .. Bunun tasarım gereği olup olmadığını bilmem gerekiyordu. Bağımlılıkların doğru kullanımını sağlamaya gelince, Steven'ın aşağıda bahsettiği gibi DI önyükleyicimle ayrı bir proje uyguladım ve burada kütüphanelerin geri kalanına atıfta bulunuyorum. Bu projeye giriş noktası uygulaması tarafından başvurulur ve tam yapının sonunda bu, gerekli tüm dll'lerin bin klasöründe olmasına neden olur. Teşekkürler!
diegohb

2
@Mark Seemann Bu soru / yanıt Microsoft'a özel mi? Tüm bağımlılıkları "uygulamanın giriş noktasına" taşıma fikrinin Maven kullanan bir Java EE / Spring projesi için mantıklı olup olmadığını bilmek istiyorum… teşekkürler!
Grégoire C

5
Bu cevap .NET dışında da geçerlidir. Robert C. Martin'in Paket Tasarımı İlkeleri bölümüne, örneğin Çevik Yazılım Geliştirme, İlkeler, Modeller ve Uygulamalar
Mark Seemann

7
@AndyDangerGagne Bileşim Kökü bir DI modelidir - Hizmet Bulucu'nun tersi . Bileşim Kökü perspektifinden, türlerin hiçbiri polimorfik değildir; Bileşim Kökü tüm türleri somut türler olarak görür ve bu nedenle Liskov İkame İlkesi bunun için geçerli değildir.
Mark Seemann

4
Genel bir kural olarak, arayüzler onları kullanan istemciler tarafından tanımlanmalıdır ( APP, bölüm 11 ), bu nedenle Kütüphane J'nin bir arayüze ihtiyacı varsa, Kütüphane J'de tanımlanmalıdır. Bu, Bağımlılık Ters Çevirme İlkesinin doğal bir sonucudur.
Mark Seemann

65

DI kapsayıcı kullanmıyor olsaydım, MVC3 uygulamamda EntityFramework kitaplığına başvurmak zorunda kalmazdım

Bir DI konteyneri kullanırken bile, MVC3 projenizin EF referansına başvurmasına izin vermeniz gerekmez, ancak bunu (örtük olarak) MVC3 projenizin içinde Kompozisyon Kökü (nesne grafiklerinizi oluşturduğunuz başlangıç ​​yolu) uygulayarak yapmayı seçersiniz. Montajları kullanarak mimari sınırlarınızı koruma konusunda çok katı iseniz, sunum mantığınızı farklı bir projeye taşıyabilirsiniz.

MVC ile ilgili tüm mantığı (denetleyiciler, vb.) Başlangıç ​​projesinden bir sınıf kitaplığına taşıdığınızda, bu sunum katmanı derlemesinin uygulamanın geri kalanından kopuk kalmasına izin verir. Web uygulaması projenizin kendisi, gerekli bir başlangıç ​​mantığına sahip çok ince bir kabuk haline gelecektir. Web uygulaması projesi, diğer tüm derlemelere referans veren Kompozisyon Kökü olacaktır.

Sunum mantığını bir sınıf kitaplığına çıkarmak, MVC ile çalışırken işleri karmaşıklaştırabilir. Denetleyiciler başlangıç ​​projesinde olmadığından (görünümler, resimler, css dosyaları muhtemelen başlangıç ​​projesinde kalmalıdır), her şeyi bağlamak daha zor olacaktır. Bu muhtemelen yapılabilir ancak kurulması daha fazla zaman alacaktır.

Olumsuz yönleri nedeniyle genellikle yalnızca Web projesinde Kompozisyon Kökünü tutmanızı tavsiye ederim. Birçok geliştirici, MVC derlemelerinin DAL derlemesine bağlı olmasını istemez, ancak bu gerçekten bir sorun değildir. Montajların bir dağıtım yapısı olduğunu unutmayın ; kodun ayrı olarak dağıtılmasına izin vermek için kodu birden çok derlemeye bölersiniz. Öte yandan mimari katman, mantıksal bir eserdir. Aynı montajda birden çok katmana sahip olmak çok olası (ve yaygın).

Bu durumda, Kompozisyon Kökü (katman) ve Sunum Katmanı'nı aynı web uygulaması projesinde (dolayısıyla aynı montajda) elde edeceğiz. Ve bu derleme DAL'ı içeren derlemeye başvursa bile, Sunum Katmanı yine de Veri Erişim Katmanına başvurmaz . Bu büyük bir ayrımdır.

Elbette, bunu yaptığımızda, derleyicinin bu mimari kuralı derleme zamanında kontrol etme yeteneğini kaybediyoruz, ancak bu bir problem olmamalı. Çoğu mimari kural aslında derleyici tarafından kontrol edilemez ve her zaman sağduyu gibi bir şey vardır. Ekibinizde herhangi bir sağduyu yoksa, her zaman kod incelemelerini kullanabilirsiniz (her takımın IMO'nun her zaman btw yapması gerekir). Mimari kurallarınızı doğrulamanıza yardımcı olan NDepend (ticari olan) gibi bir araç da kullanabilirsiniz. NDepend'i derleme sürecinize entegre ettiğinizde, bu tür mimari kuralı ihlal eden biri kodu kontrol ettiğinde sizi uyarabilir.

Bağımlılık Enjeksiyonu, İlkeler, Uygulamalar, Kalıplar kitabımın 4. bölümünde Kompozisyon Kökünün nasıl çalıştığına dair daha ayrıntılı bir tartışma okuyabilirsiniz .


Önyükleme için ayrı bir proje benim çözümümdü çünkü ndepend'e sahip değiliz ve daha önce hiç kullanmadım. Bununla birlikte, sadece 1 son uygulama olduğunda yapmaya çalıştığım şeyi başarmanın daha iyi bir yolu gibi göründüğü için buna bakacağım.
diegohb

1
Son paragraf harika bir paragraf ve katmanları ayrı meclislerde tutma konusunda ne kadar katı olduğum konusunda fikrimi değiştirmeme yardımcı olmaya başlıyor. Bir derlemede iki veya daha fazla mantıksal katmana sahip olmak, UI kodunuzda DAL sınıflarına başvurulmadığından emin olmak için kod yazımı etrafında başka işlemler (kod incelemeleri gibi) kullanırsanız, aslında iyidir ve bunun tersi de geçerlidir.
BenM

6

Bir DI kapsayıcısı kullanmasaydım, MVC3 uygulamamdaki EntityFramework kitaplığına başvurmak zorunda kalmazdım, yalnızca DAL / Repo katmanıma başvuran iş katmanıma.

"DependencyResolver" adında ayrı bir proje oluşturabilirsiniz. Bu projede tüm kütüphanelerinize referans vermelisiniz.

Artık UI Katmanı, referans alınacak Castle Windsor dışında NHibernate / EF veya UI ile ilgili olmayan başka bir kitaplığa ihtiyaç duymuyor.

Castle Windsor ve DependencyResolver'ı UI katmanınızdan gizlemek istiyorsanız, IoC kayıt defteri öğelerini çağıran bir HttpModule yazabilirsiniz.

StructureMap için sadece bir örneğim var:

public class DependencyRegistrarModule : IHttpModule
{
    private static bool _dependenciesRegistered;
    private static readonly object Lock = new object();

    public void Init(HttpApplication context)
    {
        context.BeginRequest += (sender, args) => EnsureDependenciesRegistered();
    }

    public void Dispose() { }

    private static void EnsureDependenciesRegistered()
    {
        if (!_dependenciesRegistered)
        {
            lock (Lock)
            {
                if (!_dependenciesRegistered)
                {
                    ObjectFactory.ResetDefaults();

                    // Register all you dependencies here
                    ObjectFactory.Initialize(x => x.AddRegistry(new DependencyRegistry()));

                    new InitiailizeDefaultFactories().Configure();
                    _dependenciesRegistered = true;
                }
            }
        }
    }
}

public class InitiailizeDefaultFactories
{
    public void Configure()
    {
        StructureMapControllerFactory.GetController = type => ObjectFactory.GetInstance(type);
          ...
    }
 }

DefaultControllerFactory, IoC konteynerini doğrudan kullanmaz, ancak IoC konteyner yöntemlerine yetki verir.

public class StructureMapControllerFactory : DefaultControllerFactory
{
    public static Func<Type, object> GetController = type =>
    {
        throw new  InvalidOperationException("The dependency callback for the StructureMapControllerFactory is not configured!");
    };

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            return base.GetControllerInstance(requestContext, controllerType);
        }
        return GetController(controllerType) as Controller;
    }
}

GetControllerTemsilci (Windsor bir ınstaller olmalıdır) bir StructureMap kayıt defteri ayarlanır.


1
Bunu yaptığımdan daha çok seviyorum, modüller harika. öyleyse Container.Dispose () çağrısını nereye yapacağım? Modül içindeki ApplicationEnd veya EndRequest olayı ...?
diegohb

1
@Steven Çünkü Global.asax, MVC UI Katmanınızda. HttpModule, DependencyResolver Projesinde olacaktır.
Çaylak

1
Küçük fayda, hiç kimsenin UI'da IoC konteynerini kullanamamasıdır. Yani, hiç kimse IoC Container'ı UI'de bir hizmet bulucu olarak kullanamaz.
Rookian

1
Ayrıca, kullanıcı arabiriminde derlemeye kesin referans olmadığı için geliştiricilerin UI katmanında yanlışlıkla DAL kodunu kullanmalarına izin vermez.
diegohb

1
Bootstrapper'ın jenerik kayıt API'sini kullanarak aynı şeyi nasıl yapacağımı buldum. UI projem, kayıtlarımı bağladığım bağımlılık çözümleme projesi olan Bootstrapper'a ve Core'umdaki (arabirimler için) projelere başvuruyor, ancak DI Framework'üm (SimpleInjector) bile. Dll'leri bin klasörüne kopyalamak için OutputTo nuget kullanıyorum.
diegohb

0
  • Bir bağımlılık vardır: bir nesne başka bir nesneyi başlatırsa.
  • Bağımlılık yoktur: Bir nesne bir soyutlama bekliyorsa (müteahhit enjeksiyonu, yöntem enjeksiyonu ...)
  • Assembly Referansları (dll, webservices ..) bağımlılık kavramından bağımsızdır, çünkü bir soyutlamayı çözmek ve kodu derleyebilmek için katman ona referans vermelidir.
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.