2010-06-30 15 views
5

Mam następujący kod w moim Global.aspxwtryskowa nieruchomości w sterowniku bazowym przy użyciu Ninject 2

protected override void OnApplicationStarted() 
{ 
    AreaRegistration.RegisterAllAreas(); 
    RegisterRoutes(RouteTable.Routes); 
    RegisterAllControllersIn(Assembly.GetExecutingAssembly()); 
} 

protected override IKernel CreateKernel() 
{ 
    return new StandardKernel(new ServiceModule()); 
} 

Mam także następujące Ninject Moduł:

internal class ServiceModule : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<IProductService>().To<ProductService>().InRequestScope(); 
    } 
} 

Mam też podstawowy kontroler:

public class BaseController : Controller 
{ 
    [Inject] 
    public IProductService ProductService 
    { 
     get; 
     set; 
    } 
} 

Ten kod działa. Problem, który mam, polega na tym, że chciałbym usunąć atrybut wstrzykiwania z kontrolera podstawowego i określić go w module usług Ninject. Innymi słowy, jak mam napisać regułę wiązania w ServiceModule, która mówi Ninject, aby wstrzyknąć ProductService do właściwości w kontrolerze bazowym?

Po usunięciu atrybutu otrzymam wyjątek NullReferenceException.

Odpowiedz

3

Życie w związkach opartych na konwencjach w wersji http://github.com/ninject/ninject.extensions.conventions - jeden implementuje IBindingGenerator. Jest to w dużej mierze związane z odkrywaniem interfejsów i usług.

Ogólnie rzecz biorąc, wstrzyknięcie konstruktora jest dobrym podejściem domyślnym. Jednak sposób, w jaki działa ASP.NET MVC, sprawia, że ​​jest to trudniejsze do wykonania (stąd FubuMVC itp.). Więc wtrysk nieruchomości to kolejna najlepsza opcja.

Może się okazać, że użycie OnActivation w twoim Bind może pozwolić ci zrobić wystarczająco dużo - a jeśli możesz, jest to zdecydowanie najprostsze.

Chciałbym scharakteryzować, co próbujesz zrobić jako aktywację opartą na konwencjach. Problem jest następujący:

  • decydowanie o tym, co zamierzasz wykonać automatycznie. Czy zamierzasz wstrzyknąć wszystko, co publiczne, a nie konkretne? Wszystko, o czym wie twój Kernel? O ile nie możesz wymyślić czystej definicji tego, co chcesz zrobić, proces wstrzykiwania może stać się nieprzewidywalny i trudny do zrozumienia. Kończy się debugowanie i wyjaśnianie kolegom.

  • dzięki czemu jest wydajna. Ninject dynamicznie generuje kod za kulisami, aby uczynić aktywację instancji wydajną (tj. W czasie chodzenia do klasy szukającej markerów szukających [Inject] generuje kod jeden raz, aby zrobić to, co następnie zostanie podważone tak, jakbyś napisał go jako "longhand").

Patrząc w kod, nie ma łatwego sposobu OOTB. Wygląda na to, że dodanie niestandardowego IInjectionHeuristic może załatwić sprawę.

jednak jeśli dostajesz to głęboko w pojemnikach, trzeba

  1. pauzy i sprawdzić, czy można keep it simple, nie schodząc z tej drogi
  2. przejść do ninject listy i wyszukiwania dla podobnych rzeczy
  3. jeśli nadal chcesz to zrobić, wyślij tam wiadomość.
+0

Ruben, można podać i przykład jak pójdę o pisaniu wiążącą regułę w ServiceModule który mówi Ninject wstrzyknąć ProductService do posiadłości w sterowniku bazowym? Dzięki! – Thomas

+0

@Thomas: Nigdy nie dokonałem personalizacji tej natury (a ponieważ nie wierzę, że to dobre podejście, nie chcę), niestety nie będę w stanie poświęcić na to czasu - przepraszam ... moja odpowiedź wydaje się być nieważna jak dotąd, dlaczego miałbym - to nie jest tak, że mam jakąś opinię, że moja odpowiedź została zrozumiana lub jest prawidłowa?) –

2

Rozbudowując pomysły Rubena Bartelinka, można utworzyć niestandardową implementację IInjectionHeuristic.

public class ControllerInjectionHeuristic : NinjectComponent, IInjectionHeuristic 
{ 
    private readonly IKernel kernel; 

    public BaseControllerInjectionHeuristic(IKernel kernel) 
    { 
     this.kernel = kernel; 
    } 

    public bool ShouldInject(MemberInfo member) 
    { 
     if (member.ReflectedType != typeof(BaseController)) 
     { 
      return false; 
     } 

     var propertyInfo = member.ReflectedType.GetProperty(member.Name); 
     object service = kernel.TryGet(propertyInfo.PropertyType); 

     return service != null; 
    } 
} 

ControllerInjectionHeuristic wstrzyknie żadnego majątku (usługi) na BaseController dla którego jądro jest w stanie rozwiązać tę usługę.

Zarejestruj niestandardową implementację za pomocą jądra.

var kernel = new StandardKernel(); 
kernel.Components.Add<IInjectionHeuristic, ControllerInjectionHeuristic>(); 

Kolejnym rozwiązaniem tego problemu jest użycie OnActivation. (To rozwiązanie jest niesprawdzone, ale powinno dać ci pojęcie, jak postępować).

public class ControllerModule : NinjectModule 
{ 
    public override void Load() 
    { 
     // Get all controller types. You could use 
     // Ninject.Extensions.Conventions. 
     IEnumerable<Type> controllerTypes = null; 
     foreach (var controllerType in controllerTypes) 
     { 
      Bind(controllerType).ToSelf().InRequestScope() 
       .OnActivation(ControllerActivation); 
     } 
    } 

    private static void ControllerActivation(IContext context, object obj) 
    { 
     var controller = obj as BaseController; 
     if (controller == null) 
     { 
      return; 
     } 

     controller.ProductService = context.Kernel.Get<IProductService>(); 
    } 
} 
+0

+1 Dobra robota dostarczająca próbkę, zamiast włóczęgi jak ja ! Nie jestem pewien, czy sposób konwersji zapytania PropertyInfo z MemberInfo jest prawidłowy - może powinieneś używać 'is' /' as'? Poza tym 'TryGet' zrobi pełny obiekt, który możesz odciągnąć. Chcesz operacji CanResolve (nie mogę myśleć o nazwie). W tym przypadku na pierwszy rzut oka wydaje mi się, że jest OK, ale osobiście spędziłbym kilka minut przeglądając dokumenty MSDN dla '.ReflectedType', aby dowiedzieć się, czy to ** Dokładnie ** co mam na myśli. –

+0

Pomyślałem, że będzie to dobra okazja, aby się czegoś nauczyć. Powód, dla którego robię 'var propertyInfo = member.ReflectedType.GetProperty (member.Name);' jest, ponieważ w moim projekcie testowym 'member' okazało się' RuntimePropertyInfo', które jest klasą wewnętrzną. Więc po prostu uciekłem z następną najlepszą rzeczą, o jakiej można pomyśleć. Wybrałem 'kernel.TryService (Type)' zamiast 'kernel.CanResolve (IRequest)' ponieważ było waaay prostsze niż skonstruowanie kompletnego żądania. Jednakże, zakładam, że 'ControllerInjectorHeuristic' nie będzie działał bardzo dobrze. Z pewnością nie _production-ready_. – mrydengren

Powiązane problemy