2012-02-14 10 views
5

Estoy probando Ninject y estoy modificando el código que escribí con Structure Map para ver qué tan fácil es. En este código base, tengo un gráfico de objetos que tienen diferentes configuraciones a través de los registros del Mapa de Estructura y el que se utiliza en tiempo de ejecución mediante un valor en la base de datos (en este caso para retirar un cuerpo del servicio wcf con algunos objetos inyectados) . Entonces, por ejemplo (usando el código del Mapa de Estructura):Usando nombres para discriminar instancias usando IoC

El Registro 1 configura todos los valores predeterminados para los tipos IBusinessContext, IRules e ILogger. Esto simplemente agrega los tipos GenericContext/Logger/Rules junto a las interfaces sin ninguna otra especialización.

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 juegos hasta IBusinessContext utilizar la clase SpecialisedContext y le dice al ctor utilizar SpecializedLogger. La instancia para IBusinessContext se llama "SpecializedContext".

public SpecializedRegistry() 
    { 
     // Old style syntax as it affects the default for IBusinessContext 
     // Perhaps a hint at what I'm doing? 
     InstanceOf<IBusinessContext>().Is.OfConcreteType<Contexts.SpecializedBusinessContext>().Named(SpecializedInstanceName).Ctor<ILogger>().Is<Loggers.SpecialisedLogger>(); 
    } 

Todo esto funciona como se espera en el Mapa de estructura (dependiendo de la sintaxis antigua o nueva).

Sin embargo, cuando he estado usando Ninject encuentro un problema al esperar que la instancia sin nombre sea la predeterminada (no cómo funciona Ninject, lo entiendo). Esto llevó a algunas investigaciones que sugirieron que usar instancias con nombre es A Really Bad Idea. Entiendo que hay mejores formas de hacerlo mediante el registro automático o los atributos para establecer un nombre o solicitar un cierto tipo, pero en el sistema que describo debe haber una forma de calcular qué configuración solicitar en el tiempo de ejecución en la parte superior del árbol (y deje que el marco de IoC determine el resto en función de los tipos o reglas registrados).

Entonces ... ¿estoy utilizando el concepto de IoC incorrectamente aquí esperando solicitar mi objeto de nivel superior por nombre o hay generalmente una mejor manera de hacer lo que estoy tratando de hacer? ¿Debería usar algo como MEF y tratar esto como complementos?

Insisto en que no estoy usando esto como una fábrica tonta y preguntando en cada nivel del código por una instancia de tipo x desde el contenedor, es solo la acción iniciadora.

Gracias de antemano por su tiempo y ayuda :)

Respuesta

3

No hay nada de malo en todo lo que la creación de enlaces de ninject por su nombre, si esa es la única manera de lograr lo que es lo que necesita la OMI.

Así que la sintaxis básica es:

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

O si necesita una clase llamada específica para obtener un unión diferente entonces usted puede probar:

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

Sin embargo, si la clase de llamada (I estoy hablando de la clase que tiene IBusinessContext en su ctor) proporciona un valor de configuración que determina qué tipo de concreto cargar, luego tendrá que usar un delegado:

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>(); 
} 

y la clase de llamada se verá algo como:

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 
    } 
} 

En ese ejemplo no hay necesidad de instancias con nombre, ya que tienes un método que carga el tipo de hormigón específico, sin embargo todavía se puede utilizar instancias con nombre si que quiere hacer algo como esto:

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: se me olvidó mencionar que si su valor de configuración no tiene que venir del código de llamada, entonces esto hace las cosas mucho más fácil. Su código en su lugar puede ser algo como:

// 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 
    } 
} 

Puede ser creativo y en lugar de utilizar cadenas mágicas, su método de configuración puede cargar hasta una enumeración y su método de Al() puede comprobar la igualdad contra una enumeración en lugar de una cadena, pero entiendes la idea. Esto se conoce como vinculación contextual en ninject, y puedo decir que, como usuario de SM en su momento, esto es mucho más poderoso que cualquier cosa que tuviera SM. Verifique el resto de los métodos When() y vea qué puede hacer.

+0

Gracias Aaron! Eso me ha aclarado algunas cosas :) Es muy apreciado. – NoodleAwa

Cuestiones relacionadas