NInject kullanarak bir fabrika kurmanın en iyi yolu nedir?


27

MVC3'te NInject kullanarak bağımlılık enjeksiyonunda oldukça rahatım. Bir MVC3 uygulamasında çalışırken, NInject kullanarak özel bir Denetleyici Oluşturma Fabrikası geliştirdim, böylece oluşturulan herhangi bir denetleyicinin bu Denetleyici Fabrikası aracılığıyla kendisine enjekte edilen bağımlılıkları olacaktır.

Şimdi bir windows uygulaması geliştirmeye başladım, Application Wide Dependency Injection kullanmak istiyorum. Birim Testini kolaylaştırmak için her nesne NInject aracılığıyla oluşturulmalıdır. Lütfen yaratılan her nesnenin sadece NInject Factory’de olması gerektiğinden emin olun.

Örneğin, Button_Clickolaydaki herhangi bir pencereye yazıyorsam:

TestClass testClass = new TestClass()

ve TestClassherhangi bir bağımlılığa sahip olduğunu söylüyorlar, ITesto zaman otomatik olarak çözülmesi gerekiyor. Kullanabileceğimi biliyorum:

Ikernel kernel = new StandardKenel()
//AddBinding()
TestClass testClass = kenel.get<TestClass>();

Ama bir nesne yaratmak istediğimde bunu yapmayı çok sıkıcı buluyorum. Ayrıca geliştiriciyi nesneyi belirli bir şekilde oluşturmaya zorlar. Daha iyi yapılabilir mi?

Nesne oluşturma için merkezi bir havuz alabilir miyim ve ardından her nesne oluşturma otomatik olarak bu depoyu kullanır?


1
Merhaba Pravin Patil: harika bir soru. Ne istediğinizi netleştirmek için başlığınızda küçük bir değişiklik yaptım; işareti kaçırdıysam değiştirmekten çekinmeyin.

@MarkTrapp: Uygun başlık için teşekkür ederiz. Ben bu tagline özledim ...
Pravin Patil

Küçük yan not olarak, proje "NInject" yerine "Ninject" yazıldığından. Enjekte edilmiş olmasına rağmen, bugünlerde ninja temasında biraz oynarlar. :) Cf. ninject.org
Cornelius

Yanıtlar:


12

İstemci uygulamaları için, MVP (veya MVVM) gibi bir desen adapte etmek ve formdan temel ViewModel veya Presenter'a veri bağlama kullanmak en iyisidir.

ViewModels için, standart Constructor Injection'ı kullanarak gerekli bağımlılıkları enjekte edebilirsiniz.

Uygulamanızın Kompozisyon Kökü'nde , uygulamanız için tüm nesne grafiğini bağlayabilirsiniz . Bunun için bir DI Container (Ninject gibi) kullanmanıza gerek yoktur, ancak kullanabilirsiniz.


7

Windows Forms uygulamaları genellikle şuna benzeyen bir giriş noktasına sahiptir:

    // Program.cs
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }

Bu noktayı kompozisyon kök kodunuzda yaparsanız, kodun Ninject'i bir Hizmet Konumlandırıcı gibi açık bir şekilde çağırdığı yer sayısını büyük ölçüde azaltabilirsiniz.

    // Program.cs
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        var kernel = InitializeNinjectKernel();
        Application.Run(kernel.Get<MainForm>());
    }

Bu noktadan itibaren, tüm bağımlılıklarınızı yapıcı enjeksiyon yoluyla enjekte ediyorsunuz.

public MainForm(TestClass testClass) {
    _testClass = testClass;
}

"Bağımlılığınız" birden fazla kez üretmeniz gereken bir şeyse, gerçekten ihtiyacınız olan bir fabrika:

public MainForm(IFactory<TestClass> testClassFactory) {
    _testClassFactory = testClassFactory;
}

...
var testClass = _testClassFactory.Get();

Bir defaya mahsus uygulama oluşturmak zorunda kalmamak için IFactory arayüzünü bu şekilde uygulayabilirsiniz:

public class InjectionFactory<T> : IFactory<T>, IObjectFactory<T>, IDependencyInjector<T>
{
    private readonly IKernel _kernel;
    private readonly IParameter[] _contextParameters;

    public InjectionFactory(IContext injectionContext)
    {
        _contextParameters = injectionContext.Parameters
            .Where(p => p.ShouldInherit).ToArray();
        _kernel = injectionContext.Kernel;
    }

    public T Get()
    {
        try
        {
            return _kernel.Get<T>(_contextParameters.ToArray());
        }
        catch (Exception e)
        {
            throw new Exception(
                string.Format("An error occurred while attempting to instantiate an object of type <{0}>",
                typeof(T)));
        }
    }

...
Bind(typeof (IFactory<>)).To(typeof (InjectionFactory<>));
Bind(typeof (IContext)).ToMethod(c => c.Request.ParentContext);

Lütfen, bu fabrika için tam uygulamaya sahip misiniz?
Tebo

@ColourBlend: Hayır, ancak InjectionFactory<T>uyguladığım diğer arayüzlerden kurtulursanız, sağlanan kodun iyi çalışması gerekir. Özel olarak sorunlarınız olduğu bir şey var mı?
StriplingWarrior

Bunu zaten uyguladım, sadece sınıf içinde başka ilginç şeyler olup olmadığını bilmek istiyorum.
Tebo

@Tebo: Az önce iletebileceğin bir fabrika gibi DI ile ilgili birkaç arabirim daha uyguladım Type, ancak bunun için hidrate ettiği nesnelerin Typebelirli bir jenerik tipin uygulanmasını ya da uzatılmasını garanti edecektim . Çok özel bir şey yok.
StriplingWarrior

4

Her zaman şöyle görünen herhangi bir IoC Konteyneri için bir Adaptör sarıcısı yazarım:

public static class Ioc
{
    public static IIocContainer Container { get; set; }
}

public interface IIocContainer 
{
    object Get(Type type);
    T Get<T>();
    T Get<T>(string name, string value);
    void Inject(object item);
    T TryGet<T>();
}

Özellikle Ninject için, somut Bağdaştırıcı sınıfı şöyle görünür:

public class NinjectIocContainer : IIocContainer
{
    public readonly IKernel Kernel;
    public NinjectIocContainer(params INinjectModule[] modules) 
    {
        Kernel = new StandardKernel(modules);
        new AutoWirePropertyHeuristic(Kernel);
    }

    private NinjectIocContainer()
    {
        Kernel = new StandardKernel();
        Kernel.Load(AppDomain.CurrentDomain.GetAssemblies());

        new AutoWirePropertyHeuristic(Kernel);
    }

    public object Get(Type type)
    {
        try
        {
            return Kernel.Get(type);
        }
        catch (ActivationException exception)
        {
            throw new TypeNotResolvedException(exception);
        }              
    }

    public T TryGet<T>()
    {
        return Kernel.TryGet<T>();
    }

    public T Get<T>()
    {
        try
        {
            return Kernel.Get<T>();
        }
        catch (ActivationException exception)
        {
            throw new TypeNotResolvedException(exception);
        }           
    }

    public T Get<T>(string name, string value)
    {
        var result = Kernel.TryGet<T>(metadata => metadata.Has(name) &&
                     (string.Equals(metadata.Get<string>(name), value,
                                    StringComparison.InvariantCultureIgnoreCase)));

        if (Equals(result, default(T))) throw new TypeNotResolvedException(null);
            return result;
    }

    public void Inject(object item)
    {
        Kernel.Inject(item);
    }
}

Bunu yapmanın birincil nedeni, IoC çerçevesini soyutlamaktır; bu nedenle, çerçeveler arasındaki farkın genellikle kullanımdan ziyade yapılandırmada olduğu göz önüne alındığında, istediğim zaman değiştirebilirim.

Ancak, bir bonus olarak, IoC çerçevesini doğal olarak desteklemeyen diğer çerçevelerde kullanmak çok daha kolay hale geliyor. WinForms için, örneğin, iki adımdır:

Ana yönteminizde, başka bir şey yapmadan önce bir kabı başlatmanız yeterlidir.

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        try
        {
            Ioc.Container = new NinjectIocContainer( /* include modules here */ );
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyStartupForm());
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }
}

Ve sonra başka biçimlerin türetildiği ve Kendisini enjekte eden temel bir Form'a sahip olun.

public IocForm : Form
{
    public IocForm() : base()
    {
        Ioc.Container.Inject(this);
    }
}

Bu, otomatik kablolama sezgiseline, modüllerinizde belirlenen kurallara uyan formdaki tüm özellikleri art arda enjekte etmeye çalışmalarını söyler.


Çok güzel bir çözüm ..... Bir deneyeceğim.
Pravin Patil

10
Bu, olağanüstü derecede kötü bir fikir olan bir Hizmet Bulucu: blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx
Mark Seemann

2
@MarkSeemann: Bir servis bulucu, herhangi bir yerden erişirseniz, en üst düzey nesnelerinizi olabildiğince uzağa bağlamasına izin vermek yerine, kötü bir fikirdir. Mark'ın kendi yorumunu sayfadan aşağıya doğru küçük bir şekilde okuyun: "Bu gibi durumlarda Kompozisyon Kökü'nü her bir nesneye (örneğin Sayfa) taşımaktan başka bir şey yapamazsınız ve DI Konteynerinizin bağımlılıklarınızı oradan almasına izin verin. Servis Bulucu anti-patern, ancak bunun nedeni konteynır kullanımını hala mutlak bir asgari seviyede tutmanızdır. " (Düzenleme: Bekleyin, Mark ARE! Öyleyse fark nedir?)
pdr

1
Aradaki fark, herhangi bir sınıfa uygun bir Singleton Hizmet Bulucu yapmak yerine, kod tabanınızın geri kalanını, Besteci'den koruyabilmenizdir.
Mark Seemann

2
Pdr: Deneyimlerime göre, nitelik sınıfları gibi şeylere hizmet enjekte etmeye çalışıyorsanız, endişeleri doğru şekilde ayırmazsınız. Kullandığınız çerçevenin uygun bağımlılık enjeksiyonunu kullanmayı neredeyse imkansız kıldığı durumlar vardır ve bazen bir servis bulucu kullanmaya zorlanırız, ancak kesinlikle buna kesinlikle geri dönmeden önce gerçek DI'yi almaya çalışacağım. Desen.
StriplingWarrior

1

Bağımlılık Enjeksiyonu'nu iyi kullanmak, genellikle nesneleri oluşturan kodu ve gerçek iş mantığını ayırmaya dayanır. Başka bir deyişle, ekibimin bu şekilde sık sık kullanmasını ve bir sınıf örneği oluşturmasını istememnew . Bir kez yapıldıktan sonra, bu beton tipini daha önce belirttiğiniz için, yarattığınız türü başka biri için kolayca değiştirmenin bir yolu yoktur.

Yani bunu düzeltmenin iki yolu var:

  1. Bir sınıfın ihtiyaç duyacağı örnekleri enjekte edin. Örneğinizde TestClass, Windows Form'unuza bir a enjekte edin, böylece ihtiyaç duyduğunda zaten bir örneği olacak. Ninject formunuzu başlattığında, bağımlılığı otomatik olarak oluşturur.
  2. Gerçekten durumlarda yok İhtiyacınız kadar bir örneğini oluşturmak istiyorum, iş mantığı içine bir fabrika enjekte edebilir. Örneğin, bir IKernelWindows Formunuza bir enjekte edebilir ve daha sonra bunu başlatmak için kullanabilirsiniz.TestClass . Tarzınıza bağlı olarak, bunu başarmanın başka yolları da var (fabrika sınıfı, fabrika temsilcisi vb. Enjekte etmek).

Bunu yapmak, hem test sınıfını kullanan kodu değiştirmeden, hem de TestClass türünü değiştirmenin yanı sıra, test sınıfının gerçek yapısını değiştirmeyi de kolaylaştırır .


1

Ben Ninject kullanılmaz ettik, ancak bir IoC kullanırken şeyler yaratmanın standart yolu a aracılığı bunu etmektir oluşturmak istediğiniz nesnenin türüdür. Öyleyse eğer nesne türden nesneler yaratmaya ihtiyaç duyuyorsa, yapıcı, alan / özellik olarak depolanan türden bir parametreye sahip olmalıdır . Şimdi türündeki nesneleri oluşturmak istediğinizde de çağırmak sana .Func<T>TT1T2T1Func<T1>T2T2T1Func

Bu tamamen sizi IoC çerçevenizden ayırır ve bir IoC zihniyetinde kodlamanın doğru yoludur.

Bunu yapmanın dezavantajı, elinizle kablo döşemeniz gerektiğinde can sıkıcı hale gelmesi Funcveya yaratıcınızın bir parametre gerektirmesi durumunda can sıkıcı hale gelmesidir, bu nedenle IoC Funcsizin için otomatik olarak dizilemez .

http://code.google.com/p/autofac/wiki/RelationshipTypes

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.