Search code examples
castle-windsorcastle-dynamicproxy

Registering components with castle that are dynamically created by DynamicProxy


So I've been working hard for a while to build a solution which creates certain components using nothing but Castle DynamicProxy (version 2.2) and an interceptor. Everything looks great except that at the end of all this I realized I need to register these components with the windsor container. Is this possible or has my work been for naught?

I'll fabricate 2 castle configurations to explain my problem. The first one works, while the second does not.

First config (this has been working great for a while):

<castle>
  <facilities>
    <facility
        id="factory.support"
        type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel" />
  </facilities>

  <components>
    <component
        id="Factory"
        service="Foo.IFactory, Foo"
        type="Foo.Local.LocalFactory, Foo.Local" />
    <component
        id="Loader"
        service="Foo.Contracts.ILoader, Foo.Contracts"
        type="Foo.Local.Loader, Foo.Local"
        factoryId="Factory" factoryCreate="GetLoader" />
  </components>
</castle>

Second config (I don't know what to put in the type attribute and it doesn't work without it):

<castle>
  <facilities>
    <facility
        id="factory.support"
        type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel" />
  </facilities>

  <components>
    <component
        id="Factory"
        service="Foo.IFactory, Foo"
        type="Foo.Remote.RemoteFactory, Foo.Remote" />
    <component
        id="Loader"
        service="Foo.Contracts.ILoader, Foo.Contracts"
        type="I DUNNO, WHAT'S THE TYPE?"
        factoryId="Factory" factoryCreate="GetLoader" />
  </components>
</castle>

So my fabricated configs register the factory facility, then I register a factory, then register my "ILoader" component. The "LocalFactory" creates an actual type for the ILoader component, whereas the "RemoteFactory" creates the ILoader component using dynamic proxy, creating the proxies without targets. I.e., I use the ProxyGenerator.CreateInterfaceProxyWithoutTarget method, so there is no underlying class.

So, is there any hope in registering components as per the second config?

EDIT: Unfortunately, using the fluent configuration API is not an option at the moment. So to narrow my question down, is it possible to achieve this using the XML configuration?


Solution

  • I believe this is possible via the Fluent Registration API and the "UsingFactoryMethod" mechanism. I have tried to replicate your fabricated scenario in the below test case.

    UPDATE

    This is in fact possible with XML configuration as well. The trick is just to list the interface itself as the "type" in the configuration (or, equivalently, only specify the "type", as the "service" will be set to the "type" if it is not explicitly provided). I have updated the test case below to include a "TestXml" test that uses xml configuration to achieve your desired result. The "TestFluent" test uses the fluent registration API to achieve it. FYI, I am using Castle Windsor 2.0 here, as I'm guessing that's what you're using.

    using Castle.DynamicProxy;
    using Castle.Facilities.FactorySupport;
    using Castle.MicroKernel.Registration;
    using Castle.Windsor;
    using NUnit.Framework;
    
    namespace CastleTests
    {
        public interface ILoader
        {
            void Load();
        }
    
        public interface ILoaderFactory
        {
            ILoader GetLoader();
        }
    
        public class LoaderFactory : ILoaderFactory
        {
            public ILoader GetLoader()
            {
                return GetLoaderStatic();
            }
    
            public static ILoader GetLoaderStatic()
            {
                return (ILoader) new ProxyGenerator().CreateInterfaceProxyWithoutTarget(typeof (ILoader));
            }
        }
    
        [TestFixture]
        public class DynamicFactoryTests
        {
            [Test]
            public void TestFluent()
            {
                using (var container = new WindsorContainer())
                {
                    container.AddFacility<FactorySupportFacility>();
                    container.Register(
                        Component.For<ILoader>().UsingFactoryMethod(() => LoaderFactory.GetLoaderStatic())
                        );
                    var loader = container.Resolve<ILoader>();
                    Assert.That(loader.GetType().FullName, Is.EqualTo("Castle.Proxies.ILoaderProxy"));
                }
            }
    
            [Test]
            public void TestXml()
            {
                using (var container = new WindsorContainer("factory.xml"))
                {
                    var loader = container.Resolve<ILoader>();
                    Assert.That(loader.GetType().FullName, Is.EqualTo("Castle.Proxies.ILoaderProxy"));
                }
            }
        }
    }
    

    The content of "factory.xml" is thusly:

    <castle>
      <facilities>
        <facility
            id="factory.support"
            type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel" />
      </facilities>
      <components>
        <component
            id="foo"
            service="CastleTests.ILoaderFactory, CastleTests"
            type="CastleTests.LoaderFactory, CastleTests" />
        <component
            id="bar"
            type="CastleTests.ILoader, CastleTests"
            factoryId="foo" factoryCreate="GetLoader" />
      </components>
    </castle>