Uso de contenedores secundarios de Castle Windsor para resolver un tipo con una instancia específica

Actualmente estoy usando la funcionalidad de contenedor secundario de Castle Windsor para anular el registro de un tipo en particular con una instancia específica en un método de fábrica. Estoy usando los contenedores secundarios únicamente para que el registro sea temporal para una sola resolución; en otras palabras, no quiero que el registro afecte todas las resoluciones para ese tipo.

Quizás algún código explique lo que quiero decir.

Tengo una Func que hace las veces de fábrica. Func<IReportCategory, IReportCategoryViewModel> - Le doy una implementación de un IReportCategory y devuelve una nueva instancia de un IReportCategoryViewModel. (IReportCategoryViewModel depende de IReportCategory).

Esto se registra en Castle Windsor de la siguiente manera:

        container.Register(
            Component.For<Func<IReportCategory, IReportCategoryViewModel>>().Instance(
                category => ResolveCategoryViewModelFactory(container, category)));

Dónde ResolveCategoryViewModelFactory se implementa de la siguiente manera:

    private CategoryViewModel ResolveCategoryViewModelFactory(IWindsorContainer container, IReportCategory category)
    {
        using (IWindsorContainer childContainer = new WindsorContainer())
        {
            childContainer.Register(Component.For<IReportCategory>().Instance(category));
            container.AddChildContainer(childContainer);

            return childContainer.Resolve<IReportCategoryViewModel>();
        }
    }

Lo que logra el método anterior es la resolución de IReportCategoryViewModel, inyectando la instancia específica de IReportCategory como una dependencia. Si IReportCategoryViewModel tiene otras dependencias que deben satisfacerse, el contenedor las inyecta automáticamente.

Posteriormente puedo usar el Func de la siguiente manera:

public class Test
{
    private readonly Func<IReportCategory, IReportCategoryViewModel> factory;

    public Test(Func<IReportCategory, IReportCategoryViewModel> factory)
    {
        this.factory = factory;
    }

    public void ResolveTest()
    {
        // Create a category (note: this would probably be resolved from the container in some way)
        IReportCategory category = new ReportCategory();

        // Call into the factory to resolve the view model
        IReportCategoryViewModel vm = factory(category);
    }
    ...

Pregunta: ¿Te parece adecuado hacer esto? Por la impresión que tengo, los contenedores para niños no se recomiendan en Castle Windsor: ¿hay otra forma de lograr el mismo resultado?

Gracias por tu ayuda.

preguntado el 08 de noviembre de 11 a las 15:11

2 Respuestas

Siguiendo el consejo de Krzysztof de usar Typed Factories, así es como implementé la funcionalidad anterior. ¡Es mucho más simple que usar contenedores secundarios!

En primer lugar, cree una interfaz de fábrica que defina la firma del método de fábrica:

public interface ICategoryViewModelFactory
{
    CategoryViewModel Create(ReportCategory category);
} 

A continuación, asegúrese de TypedFactoryFacility está habilitado en el contenedor:

container.AddFacility<TypedFactoryFacility>();

Finalmente, registre la interfaz de fábrica con el contenedor:

container.Register(
    Component.For<ICategoryViewModelFactory>()
        .AsFactory());

Ahora puedes inyectar ICategoryViewModelFactory en sus clases, y llame al Create() método para crear una nueva instancia de CategoryViewModel:

public class SomeClass
{
    public SomeClass(ICategoryViewModelFactory categoryViewModelFactory)
    {
        // This would probably be resolved by the container (it's like this for the example)
        ReportCategory category = new ReportCategory();

        // Get Windsor to resolve the view model using the factory
        ReportCategoryViewModel vm = categoryViewModelFactory.Create(category);

        ....

Aviso: El nombre del parámetro en el método de fábrica debe coincidir con el nombre del parámetro del constructor del objeto que crea la fábrica. En el ejemplo anterior, la interfaz de fábrica define el método:

CategoryViewModel Create(ReportCategory category)

El constructor de CategoryViewModel también debe tener el parámetro denominado "categoría":

public CategoryViewModel(ReportCategory category)

Esto se debe a que el método de fábrica es equivalente al siguiente:

container.Resolve<CategoryViewModel>(new { category = paramPassedIntoFactoryMethod });

respondido 10 nov., 11:15

Gracias por publicar el código que usó para resolver este problema. Pasé mucho tiempo buscando un ejemplo como este para ayudarme. Sin embargo, todavía estoy atascado y espero que puedas ayudar. He copiado efectivamente su código y reemplazado algunas palabras para que sea apropiado para mis objetos y cuando ejecuto la aplicación, obtengo un DependencyResolverException por mi equivalente a tu ReportCategory objeto. La excepción establece que mi modelo de vista tiene una dependencia del modelo que no se pudo resolver. Tiene ReportCatgory registrado en Castle Windsor? ¿Si es así, cómo? - Stuart Leyland-Cole

Logré que esto funcionara y publiqué mi código en la siguiente pregunta: stackoverflow.com/a/11604109/336752 - Stuart Leyland-Cole

Absolutamente hay mejores formas de hacerlo, y el código que está usando ahora tiene un error: intentará liberar todas las instancias de componentes que está resolviendo cuando deseche el contenedor secundario, por lo tanto, es posible que no se puedan usar (desechar) incluso antes de tener la oportunidad de usarlos.

Si entiendo tu explicación correctamente, se siente como un trabajo para fábricas mecanografiadas.

respondido 10 nov., 11:01

¡Las fábricas mecanografiadas son una manera mucho mejor de hacer esto! ¡Muchas gracias Krzysztof! - Tom Davis

Esto no resulta en el mismo comportamiento que los contenedores secundarios, ya que usa nombres para hacer coincidir los argumentos del constructor con los argumentos del método de fábrica, solo funciona para el tipo devuelto por la fábrica, lo que significa que cualquiera de sus dependencias no obtener la instancia pasada al método de fábrica si es necesario inyectarla. Aquí hay una esencia que he estado usando para explorar esto (que podría explicar mejor :)) gist.github.com/1357829 - GraemeF

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.