2012-02-22 11 views
7

Can Castle Windsor resolver una colección filtrada por un parámetro de cadena?Cómo resolver la recopilación con el parámetro de filtrado?

interface IViewFactory 
{ 
    IView[] GetAllViewsInRegion(string regionName); 
} 

Mi aplicación define las regiones como grupos de tipos derivados de IView. Cuando visualizo una región en particular en tiempo de ejecución, quiero resolver una instancia de cada tipo de IView dentro de ella (al estilo de Prism).

He intentado hacerlo con Castle's Typing Factory Facility, ComponentModel Construction Contributors y Handler Selectors, pero no puedo imaginar cómo asignar múltiples tipos a una cadena de una manera que Castle puede acceder, ni cómo extender Castle para verificar la cadena cuando decide qué tipos tratar de resolver y devolver en el contenedor.

Respuesta

1

¿La selección por cuerda es estrictamente necesaria? ¿Sería posible tener todas las implementaciones IView en la misma "región" implementando una interfaz dedicada que deriva de IView? Entonces podría usar WindsorContainer.ResolveAll() (pasando su IView específica de la región como T) para resolver las implementaciones para la región en cuestión (o podría usar uno de los Resoltores de Colección para realizar la inyección del constructor).

En general, cuando trato de hacer cosas como esta con Windsor, hago todo lo posible por utilizar el sistema tipográfico (y el soporte de Windsor) antes de recurrir a soluciones basadas en cadenas.

Actualización: ya se confirmó que la selección de la cadena es necesario en este caso, la mejor solución que veo es simplemente inspeccionar la lista de controladores en el núcleo, que satisfacen el servicio IView, luego filtrar a los ejecutores en el que el región (definida por atributo) coincide con lo que queremos, luego resuelve esos implementadores. Esto parece un poco hackish, pero si está de acuerdo con tener una referencia directa al contenedor en su implementación de IViewFactory, esto parece funcionar bien. A continuación se muestra un caso de prueba que pasa que demuestra la solución.

[Test] 
    public void Test() 
    { 
     using (var factory = new ViewFactory()) 
     { 

      var regionOneViews = factory.GetAllViewsInRegion("One"); 
      Assert.That(regionOneViews, Is.Not.Null); 
      Assert.That(regionOneViews, Has.Length.EqualTo(2)); 
      Assert.That(regionOneViews, Has.Some.TypeOf<RegionOneA>()); 
      Assert.That(regionOneViews, Has.Some.TypeOf<RegionOneB>()); 

      var regionTwoViews = factory.GetAllViewsInRegion("Two"); 
      Assert.That(regionTwoViews, Is.Not.Null); 
      Assert.That(regionTwoViews, Has.Length.EqualTo(1)); 
      Assert.That(regionTwoViews, Has.Some.TypeOf<RegionTwoA>()); 
     } 
    } 
} 

public interface IViewFactory 
{ 
    IView[] GetAllViewsInRegion(string regionName); 
} 

public class ViewFactory : IViewFactory, IDisposable 
{ 
    private readonly WindsorContainer _container; 

    public ViewFactory() 
    { 
     _container = new WindsorContainer(); 
     _container.Register(
      Component.For<IView>().ImplementedBy<RegionOneA>(), 
      Component.For<IView>().ImplementedBy<RegionOneB>(), 
      Component.For<IView>().ImplementedBy<RegionTwoA>() 
      ); 
    } 

    public IView[] GetAllViewsInRegion(string regionName) 
    { 
     return _container.Kernel.GetHandlers(typeof (IView)) 
      .Where(h => IsInRegion(h.ComponentModel.Implementation, regionName)) 
      .Select(h => _container.Kernel.Resolve(h.ComponentModel.Name, typeof (IView)) as IView) 
      .ToArray(); 
    } 

    private bool IsInRegion(Type implementation, 
          string regionName) 
    { 
     var attr = 
      implementation.GetCustomAttributes(typeof (RegionAttribute), false).SingleOrDefault() as RegionAttribute; 
     return attr != null && attr.Name == regionName; 
    } 

    public void Dispose() 
    { 
     _container.Dispose(); 
    } 
} 

public interface IView {} 

[Region("One")] 
public class RegionOneA : IView {} 

[Region("One")] 
public class RegionOneB : IView {} 

[Region("Two")] 
public class RegionTwoA : IView {} 

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 
public class RegionAttribute : Attribute 
{ 
    private readonly string _name; 

    public RegionAttribute(string name) 
    { 
     _name = name; 
    } 

    public string Name 
    { 
     get { return _name; } 
    } 
} 
+0

intento utilizar tipos en lugar de claves de cadena, también, pero el punto de extensión Prism Estoy trabajando con (RegionBehavior) asocia las regiones con las cadenas, por lo que no se puede ver ninguna manera alrededor de la selección por la cadena. En realidad, Prism solo espera objetos en la región, por lo que incluso la interfaz IView es una simplificación artificial de mi parte. En [revisión 1] (http://stackoverflow.com/revisions/9394391/1) di un montón de contexto para mi pregunta, pero creo que estaba asustando a la gente. –

+0

Algunas regiones se definen en complementos. Las cosas se vuelven muy complicadas y lentas si mi clase tiene que buscar conjuntos cargados para una interfaz de marcador con el nombre corto '" I "+ regionName +" View "'. Preferiría decorar clases con un atributo personalizado como 'ViewExport [RegionName =" MainRegion "]' y tipos de filtro en algo como el punto de extensión HandlersFilter de Windsor. –

+0

Bien, entiendo, no estoy familiarizado con Prism, así que no me di cuenta de que la selección de la secuencia era necesaria. Creo que algo como esto debería ser posible. Trataré de encontrar algo en mi tiempo libre hoy :-) –

Cuestiones relacionadas