Pregunta

Estoy intentando que Unity administre la creación de mis objetos y quiero tener algunos parámetros de inicialización que no se conocen hasta el tiempo de ejecución:

Por el momento, la única forma en que se me ocurre hacerlo es tener un método Init en la interfaz.

interface IMyIntf {
  void Initialize(string runTimeParam);
  string RunTimeParam { get; }
}

Luego para usarlo (en Unity) haría esto:

var IMyIntf = unityContainer.Resolve<IMyIntf>();
IMyIntf.Initialize("somevalue");

En este escenario runTimeParam El parámetro se determina en tiempo de ejecución en función de la entrada del usuario.El caso trivial aquí simplemente devuelve el valor de runTimeParam pero en realidad el parámetro será algo así como el nombre del archivo y el método de inicialización hará algo con el archivo.

Esto crea una serie de problemas, a saber, que el Initialize El método está disponible en la interfaz y se puede llamar varias veces.Establecer una bandera en la implementación y lanzar una excepción en llamadas repetidas a Initialize Parece muy torpe.

En el momento en que resuelvo mi interfaz, no quiero saber nada sobre la implementación de IMyIntf.Sin embargo, lo que sí quiero es saber que esta interfaz necesita ciertos parámetros de inicialización únicos.¿Hay alguna manera de anotar (¿atributos?) de alguna manera la interfaz con esta información y pasarla al marco cuando se crea el objeto?

Editar:Describí la interfaz un poco más.

¿Fue útil?

Solución

En cualquier lugar donde se necesite un valor de tiempo de ejecución para la construcción de una dependencia especial, Abstract Factory es la solución.

Tener métodos initialize en las interfaces olores de un Leaky abstracción .

En su caso, diría que se debe modelar la interfaz IMyIntf en cómo hay que usarla - no cómo la intención de crear implementaciones del mismo. Eso es un detalle de implementación.

Por lo tanto, la interfaz debe ser simplemente:

public interface IMyIntf
{
    string RunTimeParam { get; }
}

Ahora definir la fábrica Resumen:

public interface IMyIntfFactory
{
    IMyIntf Create(string runTimeParam);
}

Ahora puede crear una aplicación concreta de IMyIntfFactory que crea instancias concretas de IMyIntf como éste:

public class MyIntf : IMyIntf
{
    private readonly string runTimeParam;

    public MyIntf(string runTimeParam)
    {
        if(runTimeParam == null)
        {
            throw new ArgumentNullException("runTimeParam");
        }

        this.runTimeParam = runTimeParam;
    }

    public string RunTimeParam
    {
        get { return this.runTimeParam; }
    }
}

Observe cómo esto nos permite proteger los invariantes de clase mediante el uso de la palabra clave readonly. No hay métodos initialize malolientes son necesarios.

Una implementación IMyIntfFactory puede ser tan simple como esto:

public class MyIntfFactory : IMyIntfFactory
{
    public IMyIntf Create(string runTimeParam)
    {
        return new MyIntf(runTimeParam);
    }
}

En todos los consumidores donde se necesita una instancia IMyIntf, usted simplemente toma una dependencia de IMyIntfFactory si la solicita, a través de Constructor de inyección .

Cualquier DI de contenedores que se precie va a ser capaz de auto-alambre de una instancia IMyIntfFactory para usted si se registra correctamente.

Otros consejos

Por lo general, cuando se encuentra con esta situación, es necesario revisar su diseño y determinar si usted está mezclando sus objetos de estado / datos con sus servicios puros. En la mayoría (no todos) de los casos, tendrá que mantener estos dos tipos de objetos separados.

Si usted no necesita un parámetro específico del contexto pasado en el constructor, una opción es la creación de una fábrica que resuelve sus dependencias de servicio a través del constructor, y toma su parámetro de tiempo de ejecución como parámetro del método Create () ( o Generar (), construcción () o lo que usted nombre a sus métodos de fábrica).

Tener set o un método initialize () son general piensa que es un mal diseño, ya que es necesario "recordar" a llamarlos y asegurarse de que no se abren demasiado de su aplicación de estado (es decir, lo que es dejar a alguien de volver a llamar a inicializar o la incubadora?).

También me he encontrado con esta situación varias veces en entornos donde estoy creando dinámicamente objetos ViewModel basados ​​en objetos Modelo (muy bien descritos en este otro Publicación de desbordamiento de pila).

Me gustó como el Extensión ninja que le permite crear dinámicamente fábricas basadas en interfaces:

Bind<IMyFactory>().ToFactory();

No pude encontrar ninguna funcionalidad similar directamente en Unidad;así que escribí mi propia extensión para el IUnityContainer lo que le permite registrar fábricas que crearán nuevos objetos basados ​​en datos de objetos existentes, esencialmente mapeando de una jerarquía de tipos a una jerarquía de tipos diferente: UnityMappingFactory@GitHub

Con el objetivo de lograr simplicidad y legibilidad, terminé con una extensión que le permite especificar directamente las asignaciones sin declarar clases de fábrica o interfaces individuales (un ahorro de tiempo real).Simplemente agrega las asignaciones justo donde registras las clases durante el proceso de arranque normal...

//make sure to register the output...
container.RegisterType<IImageWidgetViewModel, ImageWidgetViewModel>();
container.RegisterType<ITextWidgetViewModel, TextWidgetViewModel>();

//define the mapping between different class hierarchies...
container.RegisterFactory<IWidget, IWidgetViewModel>()
.AddMap<IImageWidget, IImageWidgetViewModel>()
.AddMap<ITextWidget, ITextWidgetViewModel>();

Luego simplemente declara la interfaz de fábrica de mapeo en el constructor de CI y usa su Crear() método...

public ImageWidgetViewModel(IImageWidget widget, IAnotherDependency d) { }

public TextWidgetViewModel(ITextWidget widget) { }

public ContainerViewModel(object data, IFactory<IWidget, IWidgetViewModel> factory)
{
    IList<IWidgetViewModel> children = new List<IWidgetViewModel>();
    foreach (IWidget w in data.Widgets)
        children.Add(factory.Create(w));
}

Como ventaja adicional, cualquier dependencia adicional en el constructor de las clases asignadas también se resolverá durante la creación del objeto.

Obviamente, esto no resolverá todos los problemas, pero hasta ahora me ha sido de gran utilidad, así que pensé que debería compartirlo.Hay más documentación en el sitio del proyecto en GitHub.

No puedo responder con la terminología específica de la Unidad pero suena como se acaba de aprender acerca de la inyección de dependencia. Si es así, le insto a leer el breve, claro y lleno de información href="http://ninject.codeplex.com/wikipage?title=user%20guide&ProjectName=ninject" para Ninject .

Esto le guiará a través de los diversos opción que tienes al utilizar DI, y cómo dar cuenta de los problemas específicos que los que se enfrentará en el camino. En su caso, lo más probable es que desee utilizar el contenedor de DI para crear instancias de los objetos, y tienen ese objeto obtener una referencia a cada una de sus dependencias a través del constructor.

El tutorial también detalla cómo anotar métodos, propiedades, e incluso los parámetros utilizando atributos para distinguirlos en tiempo de ejecución.

Incluso si usted no utiliza Ninject, el tutorial le dará los conceptos y terminología de la funcionalidad que se adapte a su propósito, y usted debería ser capaz de asignar ese conocimiento a la Unidad o de otros marcos de DI (o convencer a yout para dar Ninject un intento).

Creo que lo resolví y se siente más sana, por lo que debe ser la mitad derecha:))

Me dividir en una IMyIntf unos interfaces "setter" "captador" y. Por lo tanto:

interface IMyIntf {
  string RunTimeParam { get; }
}


interface IMyIntfSetter {
  void Initialize(string runTimeParam);
  IMyIntf MyIntf {get; }
}

A continuación, la aplicación:

class MyIntfImpl : IMyIntf, IMyIntfSetter {
  string _runTimeParam;

  void Initialize(string runTimeParam) {
    _runTimeParam = runTimeParam;
  }

  string RunTimeParam { get; }

  IMyIntf MyIntf {get {return this;} }
}

//Unity configuration:
//Only the setter is mapped to the implementation.
container.RegisterType<IMyIntfSetter, MyIntfImpl>();
//To retrieve an instance of IMyIntf:
//1. create the setter
IMyIntfSetter setter = container.Resolve<IMyIntfSetter>();
//2. Init it
setter.Initialize("someparam");
//3. Use the IMyIntf accessor
IMyIntf intf = setter.MyIntf;

IMyIntfSetter.Initialize() todavía se puede llamar varias veces, pero utilizando trozos de paradigma Servicio de localización que puede envolverlo bastante bien para que IMyIntfSetter es casi una interfaz interna que es distinta de IMyIntf.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top