views:

33

answers:

1

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?

+2  A: 

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>
Stuart Lange
Thanks, that is useful information, but unfortunately, using the fluent registration is not an option for me at the moment.
joniba
I had another look and figured how to do this via xml -- see my updated answer.
Stuart Lange
Thank you sir, that looks great. So unexpectedly simple! I will check it when I get back to work on Sunday.
joniba
Thanks, glad this helped you out!
Stuart Lange
Works like a charm. Thanks again.
joniba

related questions