2012-02-14 8 views
5

Sto provando con Ninject e sto modificando il codice che ho scritto con Structure Map per vedere quanto sia facile. In questo codice base ho un grafico di oggetti che hanno diverse configurazioni tramite i registri della mappa della struttura e quello da utilizzare viene scelto in fase di esecuzione tramite un valore nel database (in questo caso per ritirare un corpo del servizio wcf con alcuni oggetti iniettati) . Ad esempio, utilizzando il codice Mappa struttura:Utilizzo dei nomi per discriminare le istanze utilizzando IoC

Il Registro di sistema 1 imposta tutti i valori predefiniti per i tipi IBusinessContext, IRules e ILogger. Questo è solo aggiungendo i tipi GenericContext/Logger/Rules a fianco delle interfacce senza altre specializzazioni.

public GenericRegistry() 
    { 
     // Set up some generic bindings here 
     For<ILogger>().Use<Loggers.GenericLogger>(); 
     For<IBusinessRule>().Use<Rules.StandardRule>(); 
     For<IBusinessContext>().Use<Contexts.GenericBusinessContext>(); 
     For<ILoggerContext>().Use<Loggers.GenericLoggerContext>(); 
    } 

Registro 2 set fino IBusinessContext utilizzare la classe SpecialisedContext e dice al ctor di utilizzare SpecializedLogger. L'istanza di IBusinessContext è denominata "SpecializedContext".

Questo funziona come previsto in Mappa struttura (in base alla sintassi vecchia o nuova).

Tuttavia, quando ho utilizzato Ninject ho riscontrato un problema con l'attesa che l'istanza senza nome fosse predefinita (non come funziona Ninject, ma ho capito). Ciò ha portato ad alcune ricerche che hanno tutte suggerito che l'uso di istanze nominate è un'idea veramente cattiva. Capisco che ci sono modi migliori per farlo usando la registrazione automatica o gli attributi per impostare un nome o richiedere un determinato tipo, ma nel sistema che sto descrivendo deve esserci un modo in fase di esecuzione per capire quale configurazione chiedere nella parte superiore dell'albero (e lasciare che la struttura IoC calcoli il resto in base a tipi o regole registrati).

Quindi ... sto semplicemente usando il concetto IoC sbagliato qui aspettandomi di chiedere il mio oggetto di alto livello per nome o c'è generalmente un modo migliore di fare quello che sto cercando di fare? Dovrei usare qualcosa come MEF e trattare tutto questo come plug-in?

Sottolineo che non sto usando questo come una fabbrica stupida e chiedendo ad ogni livello del codice un'istanza di tipo x dal contenitore, è solo l'azione di avvio.

Grazie in anticipo per il vostro tempo e aiuto :)

risposta

3

Non c'è niente di tutto ciò che di sbagliato con la creazione di binding Ninject per nome, se questo è l'unico modo per ottenere ciò è necessario IMO.

Così la sintassi di base è:

Bind<IBusinessContext>().To<ConcreteBusinessContext>().Named("XYZ"); 

O se avete bisogno di una classe specifica chiamata per ottenere un diverso legame allora si può provare:

Bind<IIBusinessContext>().To<SomeOtherConcreteBusinessContext>().WhenInjectedInto<TypeOfCallingClass>(); 

Tuttavia, se la classe di chiamata (I sto parlando della classe che ha IBusinessContext nel suo ctor) fornisce un valore di configurazione che determina quale tipo concreto da caricare, quindi sarà necessario utilizzare un delegato:

Bind<Func<string, IBusinessContext>>().ToMethod(ctx => str => DetermineWhichConcreteTypeToLoad(ctx, str)); 

//messy sudo code 
static DetermineWhichConcreteTypeToLoad(IContext ctx, string str) 
{ 
    if(str == "somevalue"){ 
     return ctx.Kernel.Get<ConcreteType1>(); 
    else 
     return ctx.Kernel.Get<ConcreteType2>(); 
} 

e la classe chiamata avrà un aspetto simile:

class DoStuff 
{ 
    Func<string, IBusinessContext>> contextFunc; 

    DoStuff(Func<string, IBusinessContext>> contextFunc) 
    { 
     this.contextFunc = contextFunc; 
    } 

    void SomeMethod() 
    { 
     var configuredValue = GetConfiguredValueSomehow(); 
     var context = contextFunc(configuredValue); //<-- this passes your config value back to ninject in the ToMethod() lambda 
     //do something with context 
    } 
} 

In questo esempio, non v'è alcuna necessità di istanze denominate, quando si dispone di un metodo che carica il tipo concreto specifico, ma è comunque possibile utilizzare le istanze denominate se si vuole fare qualcosa di simile:

Bind<IBusinessContext>().To<ConcreteBusinessContext>().Named("config1"); 
Bind<IBusinessContext>().To<SomeOtherBusinessContext>().Named("config2"); 

Bind<Func<string, IBusinessContext>>().ToMethod(ctx => str => ctx.Kernel.Get<IBusinessContext>().Named(str)); 

class DoStuff 
{ 
    Func<string, IBusinessContext>> contextFunc; 

    DoStuff(Func<string, IBusinessContext>> contextFunc) 
    { 
     this.contextFunc = contextFunc; 
    } 

    void SomeMethod() 
    { 
     var configuredValue = "config1"; 
     var context = contextFunc(configuredValue); //<-- this will passthrough "config1" to the above ToMethod() method and ask for a IBusinessContext named "config1" 

    } 
} 

EDIT: ho dimenticato di dire che se il valore di configurazione Non deve venire dal codice chiamante, allora questo rende le cose molto più facile. Il codice può invece essere simile:

// this method can just be a global method in you app somewhere 
static string GetConfigValue() 
{ 
    //something like 
    return AppSetting.Get("config"); 
} 

Bind<IBusinessContext>().To<ConcreteBusinessContext>().When(r => GetConfigValue() == "config1"); 
Bind<IBusinessContext>().To<SomeOtherBusinessContext>().When(r => GetConfigValue() == "config2"); 

class DoStuff 
{ 
    IBusinessContext context; 

    DoStuff(BusinessContext context) 
    { 
     this.context = context; 
    } 

    void SomeMethod() 
    { 
     //use the context value as you normally would 
    } 
} 

Si può essere creativi e, piuttosto che usare le stringhe di magia, il metodo di configurazione in grado di caricare fino un enum e il metodo Quando() può verificare l'uguaglianza contro un enum invece di un stringa, ma si ottiene l'idea. Questo è noto come binding contestuale in ninject, e posso dirti come un utente avido di SM, questo è molto più potente di qualsiasi altro SM. Controlla il resto dei metodi When() e vedi cosa puoi fare.

+0

Grazie Aaron! Questo ha chiarito alcune cose per me :) È molto apprezzato. – NoodleAwa

Problemi correlati