2009-08-18 16 views
17

Jestem początkującym, jeśli chodzi o DI i ninject i mam trochę problemów z o tym, kiedy powinien nastąpić właściwy zastrzyk i jak rozpocząć wiązanie .Ninject - jak i kiedy wstrzyknąć

Używam go już w mojej aplikacji internetowej i działa dobrze tam, , ale teraz chcę użyć wtrysku w bibliotece klas.

że mam klasa tak:

public class TestClass 
{ 
    [Inject] 
    public IRoleRepository RoleRepository { get; set; } 
    [Inject] 
    public ISiteRepository SiteRepository { get; set; } 
    [Inject] 
    public IUserRepository UserRepository { get; set; } 

    private readonly string _fileName; 

    public TestClass(string fileName) 
    { 
     _fileName = fileName; 
    } 

    public void ImportData() 
    { 
     var user = UserRepository.GetByUserName("myname"); 
     var role = RoleRepository.GetByRoleName("myname"); 
     var site = SiteRepository.GetByID(15); 
     // Use file etc 
    } 

} 

chcę użyć zastrzyk nieruchomości tutaj, bo muszę przechodzić w pliku w moim konstruktora. Czy mam rację, mówiąc, że jeśli potrzebuję przejść przez parametr konstruktora, nie mogę użyć iniekcji konstruktora? Jeśli mogę użyć iniekcji konstruktora z dodatkowymi parametrami, w jaki sposób przekazuję te parametry?

Mam app konsoli, który zużywa o klasie test, który wygląda jak następująco:

class Program 
{ 
    static void Main(string[] args) 
    { 
     // NinjectRepositoryModule Binds my IRoleRepository etc to concrete 
     // types and works fine as I'm using it in my web app without any 
     // problems 
     IKernel kernel = new StandardKernel(new NinjectRepositoryModule()); 

     var test = new TestClass("filename"); 

     test.ImportData(); 
    } 
} 

Moim problemem jest to, że gdy zgłoszę test.ImportData() moje repozytoria są nieważne - nie został wstrzyknięty do nich. Próbowałem tworzenia kolejnego modułu i nazywając

Bind<TestClass>().ToSelf(); 

jak myślałem, że to może rozwiązać wszystkie właściwości wtrysku w TestClass ale mam donikąd.

Jestem pewien, że jest to banalny problem, ale po prostu nie mogę się dowiedzieć, jak to zrobić.

Odpowiedz

18

Jesteś bezpośrednio newing TestClass, który Ninject nie ma możliwości przechwycenia - pamiętaj, nie ma magii jak transformacji przechwyceniem kodu new s itp

Należy robić kernel.Get<TestClass> zamiast.

W przeciwnym razie, można wstrzyknąć go po Cinew go z kernel.Inject(test);

Myślę, że artykuł w wiki, który mówi o Inject vs Get itp

Zauważ, że w ogóle, bezpośredni Get lub Inject połączenia to robiąca im się nieprzyjemna woń lokalizacji usługi, która jest antipattern. W przypadku Twojej aplikacji internetowej, NinjectHttpModule i PageBase są hakiem, który przechwytuje tworzenie obiektów - istnieją podobne przechwytujące/logiczne miejsca do przechwytywania w innych stylach aplikacji.

Re swojej Bind<TestClass>().ToSelf(), ogólnie StandardKernel ma ImplicitSelfBinding = true które sprawiają, że niepotrzebne (chyba, że ​​chcesz mieć wpływ na jego zakres, jaki należy coś innego niż .InTransientScope()).

Ostateczny punkt styli: - używasz wtrysku właściwości. Z tego powodu rzadko istnieją ku temu powody, więc powinieneś zamiast tego używać zastrzyku konstruktora.

I idź kupić Dependency Injection in .NET by @Mark Seemann, który ma tutaj stosy doskonałych stanowisk, które obejmują wiele ważnych, ale subtelnych rozważań w obszarze Dependency Injection i wokół niego.

+0

Wydaje się działać przy użyciu jądra.Inject (test). Dzięki. Z ciekawości kolejne pytanie - Jestem w nowej wersji TestClass, ponieważ muszę przekazać ciąg znaków do konstruktora. Czy istnieje sposób przekazywania parametru przy użyciu jądra.Get ()? –

+0

Tak - są przeciążenia Get i możesz je również umieścić w Bindie –

+0

Generalnie lepszym rozwiązaniem w miejsce hardwiringa jest to, co wymaga konstruktora arg, zamiast tego odwołuj się do obiektu confuigurator, który jesteś nowy, a następnie Bind podczas tworzenia modułu –

7

OK,

Znalazłem się, jak robić to, co muszę, po części dzięki komentarze Ruben. Stworzyłem nowy moduł, który zasadniczo zawiera konfigurację, której używam w bibliotece klas. W ramach tego modułu mogę albo powiązać za pomocą interfejsu zastępczego, albo mogę dodać parametr konstruktora do CustomerLoader. Poniżej znajduje się kod z pozornej aplikacji konsolowej do demonstrowania obu sposobów.

To może pomóc komuś w rozpoczęciu pracy z programem Ninject!

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Ninject.Core; 
using Ninject.Core.Behavior; 

namespace NinjectTest 
{ 
    public class Program 
    { 
     public static void Main(string[] args) 
     { 
      var kernel = new StandardKernel(new RepositoryModule(), new ProgramModule());    
      var loader = kernel.Get<CustomerLoader>(); 
      loader.LoadCustomer(); 
      Console.ReadKey(); 
     } 
    } 

    public class ProgramModule : StandardModule 
    { 
     public override void Load() 
     { 
      // To get ninject to add the constructor parameter uncomment the line below 
      //Bind<CustomerLoader>().ToSelf().WithArgument("fileName", "string argument file name"); 
      Bind<LiveFileName>().To<LiveFileName>(); 
     } 
    } 

    public class RepositoryModule : StandardModule 
    { 
     public override void Load() 
     { 
      Bind<ICustomerRepository>().To<CustomerRepository>().Using<SingletonBehavior>(); 
     } 
    } 

    public interface IFileNameContainer 
    { 
     string FileName { get; } 
    } 
    public class LiveFileName : IFileNameContainer 
    { 
     public string FileName 
     { 
      get { return "live file name"; } 
     } 
    } 


    public class CustomerLoader 
    { 
     [Inject] 
     public ICustomerRepository CustomerRepository { get; set; } 
     private string _fileName; 

     // To get ninject to add the constructor parameter uncomment the line below 
     //public CustomerLoader(string fileName) 
     //{ 
     // _fileName = fileName; 
     //} 
     public CustomerLoader(IFileNameContainer fileNameContainer) 
     { 
      _fileName = fileNameContainer.FileName; 
     } 

     public void LoadCustomer() 
     { 
      Customer c = CustomerRepository.GetCustomer(); 
      Console.WriteLine(string.Format("Name:{0}\nAge:{1}\nFile name is:{2}", c.Name, c.Age, _fileName)); 
     } 
    } 

    public interface ICustomerRepository 
    { 
     Customer GetCustomer(); 
    } 
    public class CustomerRepository : ICustomerRepository 
    { 
     public Customer GetCustomer() 
     { 
      return new Customer() { Name = "Ciaran", Age = 29 }; 
     } 
    } 
    public class Customer 
    { 
     public string Name { get; set; } 
     public int Age { get; set; } 
    } 
} 
+0

Dobrze wyglądasz - o to mi chodziło. W kontekście prawdziwej aplikacji domyślam się, że rzeczy w pliku IFilenameContainer mogą być wykonane przez coś podobnego do ogólnego, jak DocumentLocation, i będą zapełniane na najwyższym poziomie z File | Open lub wiersza poleceń itp. Ogólnie mówiąc, pojęcie zawijania informacji, które są przekazywane jako surowe dane w parametrach, do których można uzyskać informacje na wyższym poziomie, to sprawia, że ​​używanie parametrów jest mniej powszechne (użyłem go tylko raz w moim ostatnim projekcie) –

Powiązane problemy