2010-06-30 12 views
5

Ho il seguente codice nel mio Global.aspxiniezione Proprietà a controller Base utilizzando Ninject 2

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

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

ho anche avere il seguente Ninject Modulo:

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

Ho anche un controller di base:

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

Questo codice funziona. Il problema che sto avendo è che mi piacerebbe rimuovere l'attributo inject dal controller di base e specificare questo nel modulo di servizio Ninject. In altre parole, come potrei fare per scrivere una regola di binding nel ServiceModule che dice a Ninject di inserire ProductService nella proprietà nel controller di base?

Se rimuovo l'attributo, otterrò una NullReferenceException.

risposta

3

Vita di associazione basata su convenzione in http://github.com/ninject/ninject.extensions.conventions - uno strumento IBindingGenerator. Questo è in gran parte interessato alla scoperta di interfacce e servizi.

In generale, l'iniezione del costruttore è un buon approccio predefinito. Tuttavia, il modo in cui funziona ASP.NET MVC rende questo più difficile da eseguire (quindi FubuMVC ecc.). Quindi l'iniezione di proprietà è l'opzione migliore.

È possibile che utilizzando nel proprio Bind si possa consentire di fare abbastanza e, se possibile, questo è di gran lunga il più semplice.

Definire ciò che si sta tentando di fare come attivazione basata su convenzione. I problemi sono:

  • decidere cosa si intende auto-iniettare. Hai intenzione di iniettare tutto ciò che è pubblico che non è concreto? Tutto ciò che il tuo kernel sa? A meno che non si riesca a trovare una definizione chiara di ciò che si vuole fare, il processo di iniezione può diventare imprevedibile e difficile da comprendere. Finisci per fare il debug e spiegare molto ai colleghi.

  • che lo rende efficiente. Ninject genera dinamicamente il codice dietro le quinte per rendere efficiente l'attivazione di un'istanza (ad esempio, al momento di percorrere la classe alla ricerca di marcatori [Inject] genera codice una volta per fare ciò che poi viene azzittito come se lo avessi scritto a mano lunga).

Guardando nel codice, non c'è un modo semplice OOTB. Sembra che l'aggiunta di un numero personalizzato IInjectionHeuristic possa fare la differenza.

Tuttavia se stai ricevendo questo profondo in contenitori, è necessario

  1. pausa e vedere se è possibile mantenere le cose semplici non andando su questa strada
  2. andare alla mailing list Ninject e di ricerca per cose simili
  3. se vuoi ancora farlo, manda una mail lì.
+0

Ruben, puoi fornire ed esempio come farei per scrivere una regola di binding nel ServiceModule che dice a Ninject di inserire ProductService nella proprietà nel controller di base? Grazie! – Thomas

+0

@Thomas: Non ho mai fatto una personalizzazione di questa natura (e siccome non credo che sia un buon approccio non ne ho voglia), sfortunatamente non riuscirò a trovare il tempo per farlo - scusa ... (E come la mia risposta sembra essere stata giudicata senza valore fino ad ora, perché dovrei - non è come se avessi dei feedback che la mia risposta è stata capita o corretta?) –

2

Espandendo le idee di Ruben Bartelink è possibile creare un'implementazione personalizzata di 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; 
    } 
} 

Il ControllerInjectionHeuristic inietterà qualsiasi proprietà (servizio) su BaseController per il quale il kernel è in grado di risolvere il servizio.

Registrare l'implementazione personalizzata con il kernel.

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

Un'altra soluzione al problema è quello di utilizzare OnActivation. (Questa soluzione non è stata testata, ma dovrebbe darti un'idea su come procedere).

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 Bel lavoro che fornisce un campione piuttosto che vagante come me ! Non sono sicuro che il modo in cui converti la ricerca di PropertyInfo da MemberInfo sia corretto, forse dovresti usare 'is' /' as'? Inoltre, un 'TryGet' farà un vero e proprio buildup di oggetti che vorresti allontanare. Vuoi un'operazione di CanResolve (che non riesco a pensare al nome di). In questo caso credo che a prima vista sia ok, ma personalmente passerei qualche minuto a esaminare i documenti MSDN per '.ReflectedType' per capire se è ** Esattamente ** cosa intendo. –

+0

Ho pensato che sarebbe stata una buona opportunità per imparare qualcosa. La ragione per cui eseguo 'var propertyInfo = member.ReflectedType.GetProperty (member.Name);' è perché, nel mio progetto di test, 'member' si è rivelato un' RuntimePropertyInfo', che è una classe interna. Quindi ho appena eseguito la prossima cosa migliore a cui si possa pensare. Ho scelto 'kernel.TryService (Type)' invece di 'kernel.CanResolve (IRequest)' perché era più facile che costruire una richiesta completa. Tuttavia, suppongo che 'ControllerInjectorHeuristic' non si esibirà molto bene. Non è sicuramente pronto per la produzione. – mrydengren

Problemi correlati