Como conecto as várias partes do meu código DI do Windsor da API da Web?

Como conecto as várias partes do meu código DI Castle Windsor da API da Web para que o roteamento do Controlador selecione a implementação correta da interface?

Nota: Após várias partidas / becos sem saída e vitórias parciais (aqui eaqui eaqui), Vou recompensar o mais rápido possível o máximo500 pontos. Mas só vou dar uma resposta realmente boa - IOW, que seja clara o suficiente para que eu possa entendê-la e "conectá-la" ao meu projeto, para que eu possa conectar uma determinada classe concreta a um Controlador específico.

Aqui não vai nada: Eu tenho um projeto de API da Web ("MVC"). Realmente, porém, este projeto de servidor não possui "V" (Exibir), portanto, talvez um acrônimo melhor seja MRC (Modelo / Repositório / Controlador).

De qualquer forma, estou tentando adicionar DI usando o Castle Windsor.

Eu entro e cavo o conceito de trocar classes concretas por meio de argumentos da interface do construtor. Apenas como implementar essa funcionalidade, no entanto,

tem sido uma fera com a qual estou lutando, e estou bastante machucada e sangrenta no momento, com cabelos desgrenhados e narinas incrustadas de lama.

Acho que tenho a maior parte do código necessário - para começar, pelo menos. Com o DI em mente, agora tenho uma pasta "DIPlumbing" e uma pasta "DIInstallers". A pasta "DIPlumbing" contém duas classes: WindsorCompositionRoot.cs e WindsorControllerFactory.cs.

A pasta "DIInstallers" possui, por enquanto, três arquivos, a saber ISomethingProvider.cs, SomethingProvider.cs e SomethingProviderInstaller.cs

SomethingProviderInstaller parece ser a chave para conectar as interfaces / classes no DIInstallers às coisas na pasta DIPlumbing.

(Também modifiquei Global.asax.cs para substituir os negócios de roteamento de controlador padrão pela substituição de Castle Windsor).

Mas estou confuso sobre quais classes devo colocar na pasta DIInstallers. Supõe-se que eles substituam meus Repositórios (que também possuem uma interface e uma classe concreta que implementa essa interface para cada modelo)? IOW, devo mover basicamente meu código de repositório para a pasta DIInstallers - e depois me livrar das unidades IRepository e Repository?

Obviamente, isso faria com que as alterações necessárias fossem feitas nas classes Controller, que a partir de agora fazem referência às classes Repository.

Ou as classes Repositórios e DIInstallers coexistem? Em caso afirmativo, qual é a conexão / afiliação entre Controladores, Instaladores e Repositórios?

Parece que quanto mais eu leio DI e Castle Windsor, mais confuso fico. Não sei se sou muito densa para isso, ou se estou tentando tornar mais difícil do que é, ou se estilos conflitantes de usá-lo / apresentá-lo são o problema. A conclusão é: estou preso na areia movediça e preciso do Johnny Quest para estender uma haste de bambu resistente.

A melhor resposta de todas, talvez, e provavelmente muito a pedir, seria uma representação visual de como todos esses componentes - controladores, modelos, repositórios, instaladores, Global.asax.cs, raízes de composição, fábricas, fornecedores, etc. , se relacionam.

Para fins de "divulgação completa", adicionarei o que espero que sejam os principais elementos do meu código abaixo para mostrar o que tenho e como ele (espero) se conecta.

Raiz da composição:

public class WindsorCompositionRoot : IHttpControllerActivator
{
    private readonly IWindsorContainer container;

    public WindsorCompositionRoot(IWindsorContainer container)
    {
        this.container = container;
    }

    public IHttpController Create(
        HttpRequestMessage request,
        HttpControllerDescriptor controllerDescriptor,
        Type controllerType)
    {
        var controller =
            (IHttpController)this.container.Resolve(controllerType);

        request.RegisterForDispose(
            new Release(
                () => this.container.Release(controller)));

        return controller;
    }

    private class Release : IDisposable
    {
        private readonly Action release;

        public Release(Action release)
        {
            this.release = release;
        }

        public void Dispose()
        {
            this.release();
        }
    }
}

Fábrica do controlador:

public class WindsorControllerFactory : DefaultControllerFactory
{
    private readonly IKernel kernel;

    public WindsorControllerFactory(IKernel kernel)
    {
        this.kernel = kernel;
        //According to my understanding of http://docs.castleproject.org/Windsor.Typed-Factory-Facility.ashx, I might need this:
        kernel.AddFacility<TypedFactoryFacility>();
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
        }
        return (IController)kernel.Resolve(controllerType);
    }

    public override void ReleaseController(IController controller)
    {
        kernel.ReleaseComponent(controller);
    }

// Nota: Esperamos que o "Algo" abaixo seja eventualmente "Departamentos" e outras classes agora representadas em Modelos e seus Repositórios e Controladores correspondentes

ISomethingProvider:

public interface ISomethingProvider
{
    // These are placeholder methods; I don't know which I will need yet...
    //bool Authenticate(string username, string password, bool createPersistentCookie);
    //void SignOut();
}

AlgoProvedor:

public class SomethingProvider : ISomethingProvider
{
    // TODO: Implement methods in ISomethingProvider, once they have been added
}

SomethingProviderInstaller:

public class SomethingProviderInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
                               .BasedOn(typeof(ISomethingProvider))
                               .WithServiceAllInterfaces());
        // From http://app-code.net/wordpress/?p=676; see also http://devlicio.us/blogs/krzysztof_kozmic/archive/2009/12/24/castle-typed-factory-facility-reborn.aspx
        container.AddFacility<TypedFactoryFacility>();
        container.Register(Component.For<IMyFirstFactory>().AsFactory()); 
    }
}

Controlador:

public class DepartmentsController : ApiController
{
    private readonly IDepartmentRepository _deptsRepository;

    public DepartmentsController(IDepartmentRepository deptsRepository)
    {
        if (deptsRepository == null)
        {
            throw new ArgumentNullException("deptsRepository is null");
        }
        _deptsRepository = deptsRepository;
    }

    public int GetCountOfDepartmentRecords()
    {
        return _deptsRepository.Get();
    }

    public IEnumerable<Department> GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch)
    {
        return _deptsRepository.Get(ID, CountToFetch);
    }
. . .
}

IRepository:

public interface IDepartmentRepository
{
    int Get();
    IEnumerable<Department> Get(int ID, int CountToFetch);
}

Repositório:

public class DepartmentRepository : IDepartmentRepository
{
    private readonly List<Department> departments = new List<Department>();

    public DepartmentRepository()
    {
        using (var conn = new OleDbConnection(
            @"Provider=Microsoft.ACE.OLEDB.12.0;[bla]"))
        {
            using (var cmd = conn.CreateCommand())
            {
                cmd.CommandText = "SELECT td_department_accounts.dept_no, IIF(ISNULL(t_accounts.name),'No Name provided',t_accounts.name) AS name FROM t_accounts INNER JOIN td_department_accounts ON t_accounts.account_no = td_department_accounts.account_no ORDER BY td_department_accounts.dept_no";
                cmd.CommandType = CommandType.Text;
                conn.Open();
                int i = 1;
                using (OleDbDataReader oleDbD8aReader = cmd.ExecuteReader())
                {
                    while (oleDbD8aReader != null && oleDbD8aReader.Read())
                    {
                        int deptNum = oleDbD8aReader.GetInt16(0);
                        string deptName = oleDbD8aReader.GetString(1);
                        Add(new Department { Id = i, AccountId = deptNum, Name = deptName });
                        i++;
                    }
                }
            }
        }
    }

    public int Get()
    {
        return departments.Count;
    }

    private Department Get(int ID) // called by Delete()
    {
        return departments.First(d => d.Id == ID);
    }

    public IEnumerable<Department> Get(int ID, int CountToFetch)
    {
        return departments.Where(i => i.Id > ID).Take(CountToFetch);
    }
. . .
}

Global.asax.cs:

public class WebApiApplication : System.Web.HttpApplication
{
    private static IWindsorContainer container;

    protected void Application_Start()
    {

        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        BootstrapContainer();

        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }

    private static void BootstrapContainer()
    {
        container = new WindsorContainer().Install(FromAssembly.This());
        var controllerFactory = new WindsorControllerFactory(container.Kernel);

        ControllerBuilder.Current.SetControllerFactory(controllerFactory);

        GlobalConfiguration.Configuration.Services.Replace(
            typeof(IHttpControllerActivator), new WindsorCompositionRoot(container));
    }

    protected void Application_End()
    {
        container.Dispose();
    }
ATUALIZAR

Ao tentar executar o servidor, para que ele pudesse testá-lo com o Fiddler2 para ver exatamente o que está sendo transmitido, ele falhou nessa linha no WindsorControllerFactory:

public WindsorControllerFactory(IKernel kernel)
{
    this.kernel = kernel;
    kernel.AddFacility<TypedFactoryFacility>(); <-- throws exception here
}

... com "System.ArgumentException não foi tratado pelo código do usuário HResult = -2147024809 Mensagem = O recurso do tipo 'Castle.Facilities.TypedFactory.TypedFactoryFacility' já foi registrado no contêiner. Somente uma instalação de um determinado tipo pode existir no contêiner. Fonte = Castle.Windsor StackTrace: no Castle.MicroKernel.DefaultKernel.AddFacility (chave String, recurso IFacility) no Castle.MicroKernel.DefaultKernel.AddFacility (recurso IFacility) no Castle.MicroKernel.DefaultKernel.AddFacilityT no HandheldServer.DIPlumbing.WindsorControllerFator. ctor (kernel do IKernel) em c: \ HandheldServer \ HandheldServer \ DIPlumbing \ WindsorControllerFactory.cs: linha 28 em HandheldServer.WebApiApplication.BootstrapContainer () em c: \ HandheldServer \ HandheldServer \ Global.asax.cs: linha 69 em HandheldServer.WebApiApplication. Application_Start () em c: \ HandheldServer \ HandheldServer \ Global.asax.cs: linha 39"

ATUALIZAÇÃO 2

Em resposta à resposta de Cristiano:

Você está dizendo que devo adicionar os dois arquivos a seguir na minha pasta Installers (eu já tenho uma pasta DIInstallers)

PlatypusInstallerFactory.cs:

public class PlatypusInstallerFactory : InstallerFactory
{
    public override IEnumerable<Type> Select(IEnumerable<Type> installerTypes)
    {
        var windsorInfrastructureInstaller = installerTypes.FirstOrDefault(it => it == typeof(WindsorInfrastructureInstaller));

        var retVal = new List<Type>();
        retVal.Add(windsorInfrastructureInstaller);
        retVal.AddRange(installerTypes
            .Where(it =>
                typeof(IWindsorInstaller).IsAssignableFrom(it) &&
                !retVal.Contains(it)
                ));

        return retVal;
    }
}

WindsorInfrastructureInstaller.cs:

public class WindsorInfrastructureInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddFacility<TypedFactoryFacility>();
    }
}

No seu global.asax, você criará e usará o seu instalador de fábrica da seguinte maneira

   var installerFactory = new PlatypusInstallerFactory();
   container.Install(FromAssembly.This(installerFactory));

Se sim, o que isso fará por mim? O acima descrito registra automaticamente minhas classes Controller e / ou Repository?

ATUALIZAÇÃO 3

Agora estou usando muito código de [http://blog.kerbyyoung.com/2013/01/setting-up-castle-windsor-for-aspnet.html#comment-form]

As partes principais são, eu acho:

global.asax.cs:

private static IWindsorContainer _container;

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    ConfigureWindsor(GlobalConfiguration.Configuration);
}

public static void ConfigureWindsor(HttpConfiguration configuration)
{
    _container = new WindsorContainer();
    _container.Install(FromAssembly.This());
    _container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
    var dependencyResolver = new WindsorDependencyResolver(_container);
    configuration.DependencyResolver = dependencyResolver;
}  

WindsorDependencyResolver.cs:

namespace HandheldServer
{
    public class WindsorDependencyResolver : System.Web.Http.Dependencies.IDependencyResolver
    {
        private readonly IWindsorContainer _container;

        public WindsorDependencyResolver(IWindsorContainer container)
        {
            _container = container;
        }

        public IDependencyScope BeginScope()
        {
            return new WindsorDependencyScope(_container);
        }

        public object GetService(Type serviceType)
        {
            if (!_container.Kernel.HasComponent(serviceType))
            {
                return null;
            }
            return this._container.Resolve(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            if (!_container.Kernel.HasComponent(serviceType))
            {
                return new object[0];
            }

            return _container.ResolveAll(serviceType).Cast<object>();
        }

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

    public class WindsorDependencyScope : IDependencyScope
    {
        private readonly IWindsorContainer _container;
        private readonly IDisposable _scope;

        public WindsorDependencyScope(IWindsorContainer container)
        {
            this._container = container;
            this._scope = container.BeginScope(); 
        }

        public object GetService(Type serviceType)
        {
            if (_container.Kernel.HasComponent(serviceType))
            {
                return _container.Resolve(serviceType);
            }
            else
            {
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return this._container.ResolveAll(serviceType).Cast<object>();
        }

        public void Dispose()
        {
            this._scope.Dispose();
        }
    }

    public class ApiControllersInstaller : IWindsorInstaller
    {
        public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
        {
            container.Register(Classes.FromThisAssembly() // should it be Types instead of Classes?
             .BasedOn<ApiController>()
             .LifestylePerWebRequest());
        }
    }

    // This idea from https://github.com/argeset/set-locale/blob/master/src/client/SetLocale.Client.Web/Configurations/IocConfig.cs
    public class ServiceInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(
                Component.For<IDeliveryItemRepository>().ImplementedBy<DeliveryItemRepository>().LifestylePerWebRequest(),
                Component.For<IDeliveryRepository>().ImplementedBy<DeliveryRepository>().LifestylePerWebRequest(),
                Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository>().LifestylePerWebRequest(),
                Component.For<IExpenseRepository>().ImplementedBy<ExpenseRepository>().LifestylePerWebRequest(),
                Component.For<IInventoryItemRepository>().ImplementedBy<InventoryItemRepository>().LifestylePerWebRequest(),
                Component.For<IInventoryRepository>().ImplementedBy<InventoryRepository>().LifestylePerWebRequest(),
                Component.For<IItemGroupRepository>().ImplementedBy<ItemGroupRepository>().LifestylePerWebRequest());
        }
    }
}
ATUALIZAÇÃO 4

Essa questão&nbsp;provavelmente é muito geral para SO, então eu postei em "Programadores"

ATUALIZAÇÃO 5

Nota: De acordo com "The DI Whisperer" (Mark Seemann), o IDependencyResolver não deve ser usado, porque falta um método de liberação (p. 207 de seu livro)