Search code examples
c#castle-windsorfactory-pattern

Windsor TypedFactoryFacility broken?


I'm trying to create a typed factory as per Windsor's example. This is my factory interface:

public interface ICustomJsonResultFactory
{
    JsonResult Create();
}

In my global.asax, I'm setting up the Windsor container by first adding the following two facilities:

.AddFacility<FactorySupportFacility>()
.AddFacility<TypedFactoryFacility>();

After that's done, I register my components:

Component.For<JsonResult>()
    .ImplementedBy<CustomJsonResult>()
    .LifestyleTransient(),
Component.For<ICustomJsonResultFactory>()
    .AsFactory())

One of my controllers has a ICustomJsonResultFactory property. When I try resolving the controller, I get the following in my logs:

Castle.Core.DependencyResolution: DEBUG 8 - 
    Client:     DefaultTypedFactoryComponentSelector / ITypedFactoryComponentSelector
    Model:      Dependency 'getMethodsResolveByName' type 'System.Boolean'
    Dependency: True
Castle.Core.DependencyResolution: DEBUG 8 - 
    Client:     DefaultTypedFactoryComponentSelector / ITypedFactoryComponentSelector
    Model:      Dependency 'fallbackToResolveByTypeIfNameNotFound' type 'System.Boolean'
    Dependency: False
Castle.Core.DependencyResolution: DEBUG 8 - 
    Client:     TypedFactoryInterceptor
    Model:      Dependency 'kernel' type 'Castle.MicroKernel.IKernelInternal'
    Dependency: Castle.MicroKernel.DefaultKernel
Castle.Core.DependencyResolution: DEBUG 8 - 
    Client:     TypedFactoryInterceptor
    Model:      Dependency 'componentSelector' type 'Castle.Facilities.TypedFactory.ITypedFactoryComponentSelector'
    Dependency: Castle.Facilities.TypedFactory.DefaultTypedFactoryComponentSelector
A first chance exception of type 'System.ArgumentException' occurred in Castle.Windsor.dll
A first chance exception of type 'System.ArgumentException' occurred in Castle.Windsor.dll
A first chance exception of type 'Castle.MicroKernel.ComponentActivator.ComponentActivatorException' occurred in Castle.Windsor.dll

I think this part is important, so I'm pointing it out:

Castle.Windsor Warning: 0 : Exception when resolving optional dependency Dependency 'JsonResultFactory' type 'CompanyName.Web.Mvc.ICustomJsonResultFactory' on component CompanyName.Web.OrderProcessing.Controllers.PickTicketController., Castle.MicroKernel.ComponentActivator.ComponentActivatorException: ComponentActivator: could not proxy CompanyName.Web.Mvc.ICustomJsonResultFactory ---> System.ArgumentException: Component CompanyName.Web.Mvc.ICustomJsonResultFactory is not a typed factory. TypedFactoryInterceptor only works with typed factories.

   at Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.SetInterceptedComponentModel(ComponentModel target)
at Castle.Windsor.Proxy.AbstractProxyFactory.SetOnBehalfAware(IOnBehalfAware onBehalfAware, ComponentModel target)
at Castle.Windsor.Proxy.AbstractProxyFactory.ObtainInterceptors(IKernel kernel, ComponentModel model, CreationContext context)
at Castle.Windsor.Proxy.DefaultProxyFactory.Create(IKernel kernel, Object target, ComponentModel model, CreationContext context, Object[] constructorArguments)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstance(CreationContext context, ConstructorCandidate constructor, Object[] arguments)
--- End of inner exception stack trace ---
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstance(CreationContext context, ConstructorCandidate constructor, Object[] arguments)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.Instantiate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.InternalCreate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.AbstractComponentActivator.Create(CreationContext context, Burden burden)
at Castle.MicroKernel.Lifestyle.AbstractLifestyleManager.CreateInstance(CreationContext context, Boolean trackedExternally)
at Castle.MicroKernel.Lifestyle.SingletonLifestyleManager.Resolve(CreationContext context, IReleasePolicy releasePolicy)
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, Burden& burden)
at Castle.MicroKernel.Handlers.ExtendedHandler.InvokeResolvePipeline(Int32 extensionIndex, ResolveInvocation invocation)
at Castle.MicroKernel.Handlers.ExtendedHandler.<>c__DisplayClass6.<InvokeResolvePipeline>b__5()
at Castle.MicroKernel.Handlers.ResolveInvocation.Proceed()
at Castle.MicroKernel.Handlers.ComponentLifecycleExtension.Intercept(ResolveInvocation invocation)
at Castle.MicroKernel.Handlers.ExtendedHandler.InvokeResolvePipeline(Int32 extensionIndex, ResolveInvocation invocation)
at Castle.MicroKernel.Handlers.ExtendedHandler.Resolve(CreationContext context, Boolean instanceRequired)
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context)
at Castle.MicroKernel.Resolvers.DefaultDependencyResolver.ResolveFromKernelByType(CreationContext context, ComponentModel model, DependencyModel dependency)
at Castle.MicroKernel.Resolvers.DefaultDependencyResolver.ResolveFromKernel(CreationContext context, ComponentModel model, DependencyModel dependency)
at Castle.MicroKernel.Resolvers.DefaultDependencyResolver.ResolveCore(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
at Castle.MicroKernel.Resolvers.DefaultDependencyResolver.Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.ObtainPropertyValue(CreationContext context, PropertySet property, IDependencyResolver resolver)

I've tried adding the Release method to the controller per Windsor's example. I also tried having the factory's Create return an interface instead of a JsonResult. I've tried going through the Windsor source using ILSpy to no avail.

What am I doing wrong? I think I'm following the example pretty closely... is the problem in my code, or in Windsor?

I'm using version 3.2.0 of the various Castle libraries with .Net 4.5.


Solution

  • Turns out I was installing the facility after registering my factory component. I thought it was before all my components, but I was mistaken. You have to install the facility first. I wrongly expected an exception in AsFactory() if the facility wasn't yet installed.

    Here is a complete program that reproduces the issue.

    public interface IFooFactory {   IFoo Create();   }
    public interface IFoo        {   void DoFoo();    }
    
    public class Foo : IFoo
    {
        public void DoFoo()
        {
            Console.WriteLine("I pity the foo");
        }
    }
    
    public class Bar
    {
        public IFooFactory MyFooFactory { get; set; }
    
        public void DoBar()
        {
            var foo = MyFooFactory.Create();
            foo.DoFoo();
        }
    
        static void Main(string[] args)
        {
            var container = new WindsorContainer();
            container.Register(
                Component.For<Bar>().ImplementedBy<Bar>().LifestyleTransient(),
                Component.For<IFoo>().ImplementedBy<Foo>().LifestyleTransient(),
                Component.For<IFooFactory>().AsFactory());
            container.AddFacility<TypedFactoryFacility>();
            var bar = container.Resolve<Bar>();
            bar.DoBar();
        }
    }