views:

38

answers:

0

When using the ArrayResolver, if I register multiple implementations of an interface and a class that depends on an array of said interfaces, I expect the array resolver to inject all the interface implementations that can be successfully resolved. If a registered implementation of that interface cannot be resolved, it will not be injected into the dependent class (obviously), and the container will not throw an exception.

Starting with Windsor 2.5.1, the ArrayResolver will throw an exception in this situation, but only if the class it is trying to instantiate has multiple constructors.

I am not sure if this change in behavior was intentional or not. I do not see anything in the 2.5.1 breaking changes document that would lead me to believe that this was intentional.

Please see the below test case:

using System;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.Resolvers.SpecializedResolvers;
using Castle.Windsor;
using NUnit.Framework;

namespace CastleTests
{
    [TestFixture]
    public class TestArrayResolver
    {
        /// <summary>
        /// In this test, we register two implementors of IDependency, and a "DependsOnArray" class
        /// that depends on an array of IDependency.  The "UnresolvalbeDependency" cannot be
        /// resolved (because its dependency on "IUnimplementedDependency" is not satisfied)
        /// Hence, only the "ResolvableDependency" is injected into "DependsOnArray"
        /// </summary> 
        [Test]
        public void ArrayResolution_UnresolvableDependencyIsNotIncluded()
        {
            using (var container = new WindsorContainer())
            {
                container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel, false));
                container.Register(
                    Component.For<IDependency>().ImplementedBy<ResolvableDependency>(),
                    Component.For<IDependency>().ImplementedBy<UnresolvalbeDependency>(),
                    Component.For<IDependOnArray>().ImplementedBy<DependsOnArray>()
                    );
                container.Resolve<IDependOnArray>();
            }
        }

        /// <summary>
        /// This test is identical to the above, except instead of registering "UnresolvableDependency"
        /// we register "UnresolvalbeDependencyWithPrimitiveConstructor", which is identical to
        /// "UnresolvableDependency", except for the fact that it has a second constructor acceptin a string.
        /// 
        /// I would expect this test to behave the same way as the above test, which it does in windsor 2.5,
        /// however in 2.5.1 and later, it fails with the following error message:
        /// 
        /// Castle.MicroKernel.ComponentActivator.ComponentActivatorException : Could not find resolvable constructor for CastleTests.UnresolvalbeDependencyWithAdditionalConstructor. Make sure all required dependencies are provided.
        /// </summary> 
        [Test]
        public void ArrayResolution_UnresolvableDependencyCausesResolutionFailure()
        {
            using (var container = new WindsorContainer())
            {
                container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel, true));
                container.Register(
                    Component.For<IDependency>().ImplementedBy<ResolvableDependency>(),
                    Component.For<IDependency>().ImplementedBy<UnresolvalbeDependencyWithPrimitiveConstructor>(),
                    Component.For<IDependOnArray>().ImplementedBy<DependsOnArray>()
                    );
                container.Resolve<IDependOnArray>();
            }
        }

        /// <summary>
        /// this test fails with the same error as the above test, indicating that the issue is not related to the
        /// primitive vs. service nature of the second cosntructor's parameter
        /// </summary>
        [Test]
        public void ArrayResolution_UnresolvableDependencyCausesResolutionFailure_ServiceConstructor()
        {
            using (var container = new WindsorContainer())
            {
                container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel, true));
                container.Register(
                    Component.For<IDependency>().ImplementedBy<ResolvableDependency>(),
                    Component.For<IDependency>().ImplementedBy<UnresolvalbeDependencyWithAdditionalServiceConstructor>(),
                    Component.For<IDependOnArray>().ImplementedBy<DependsOnArray>()
                    );
                container.Resolve<IDependOnArray>();
            }
        }
    }

    public interface IDependency
    {
    }

    public class ResolvableDependency : IDependency
    {
    }

    public interface IUnimplementedDependency
    {
    }

    public class UnresolvalbeDependency : IDependency
    {
        public UnresolvalbeDependency(IUnimplementedDependency unimplementedDependency)
        {
        }
    }

    public interface IOtherUnimplementedDependency
    {
    }

    public class UnresolvalbeDependencyWithAdditionalServiceConstructor : IDependency
    {
        public UnresolvalbeDependencyWithAdditionalServiceConstructor(IUnimplementedDependency unimplementedDependency)
        {
        }

        public UnresolvalbeDependencyWithAdditionalServiceConstructor(IOtherUnimplementedDependency dep)
        {
        }
    }

    public class UnresolvalbeDependencyWithPrimitiveConstructor : IDependency
    {
        public UnresolvalbeDependencyWithPrimitiveConstructor(IUnimplementedDependency unimplementedDependency)
        {
        }

        public UnresolvalbeDependencyWithPrimitiveConstructor(string str)
        {
        }
    }

    public interface IDependOnArray
    {
    }

    public class DependsOnArray : IDependOnArray
    {
        public DependsOnArray(params IDependency[] dependencies)
        {
            foreach (var dependency in dependencies)
            {
                Console.WriteLine(dependency.GetType().Name);
            }
        }
    }
}

The full stack trace of the exception is below:

at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.SelectEligibleConstructor(CreationContext context) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\ComponentActivator\DefaultComponentActivator.cs: line 288
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.Instantiate(CreationContext context) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\ComponentActivator\DefaultComponentActivator.cs: line 104
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.InternalCreate(CreationContext context) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\ComponentActivator\DefaultComponentActivator.cs: line 85
at Castle.MicroKernel.ComponentActivator.AbstractComponentActivator.Create(CreationContext context) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\ComponentActivator\AbstractComponentActivator.cs: line 75
at Castle.MicroKernel.Lifestyle.SingletonLifestyleManager.Resolve(CreationContext context) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\Lifestyle\SingletonLifestyleManager.cs: line 50
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\Handlers\DefaultHandler.cs: line 61
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context, Boolean instanceRequired) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\Handlers\AbstractHandler.cs: line 770
at Castle.MicroKernel.Handlers.AbstractHandler.TryResolve(CreationContext context) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\Handlers\AbstractHandler.cs: line 376
at Castle.MicroKernel.DefaultKernel.TryResolveComponent(IHandler handler, Type service, IDictionary additionalArguments) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\DefaultKernel.cs: line 899
at Castle.MicroKernel.DefaultKernel.ResolveAll(Type service, IDictionary arguments) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\DefaultKernel_Resolve.cs: line 293
at Castle.MicroKernel.Resolvers.SpecializedResolvers.ArrayResolver.Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\Resolvers\SpecializedResolvers\ArrayResolver.cs: line 71
at Castle.MicroKernel.Resolvers.DefaultDependencyResolver.Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\Resolvers\DefaultDependencyResolver.cs: line 239
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateConstructorArgumentsCore(ConstructorCandidate constructor, Object[] arguments, CreationContext context, Type[] signature) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\ComponentActivator\DefaultComponentActivator.cs: line 367
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateConstructorArguments(ConstructorCandidate constructor, CreationContext context, Type[]& signature) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\ComponentActivator\DefaultComponentActivator.cs: line 344
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.Instantiate(CreationContext context) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\ComponentActivator\DefaultComponentActivator.cs: line 107
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.InternalCreate(CreationContext context) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\ComponentActivator\DefaultComponentActivator.cs: line 85
at Castle.MicroKernel.ComponentActivator.AbstractComponentActivator.Create(CreationContext context) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\ComponentActivator\AbstractComponentActivator.cs: line 75
at Castle.MicroKernel.Lifestyle.SingletonLifestyleManager.Resolve(CreationContext context) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\Lifestyle\SingletonLifestyleManager.cs: line 50
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\Handlers\DefaultHandler.cs: line 61
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context, Boolean instanceRequired) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\Handlers\AbstractHandler.cs: line 770
at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\MicroKernel\DefaultKernel.cs: line 880
at Castle.Windsor.WindsorContainer.Resolve(Type service) in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\Windsor\WindsorContainer.cs: line 889
at Castle.Windsor.WindsorContainer.Resolve() in c:\TeamCity\buildAgent\work\1ab5e0b25b145b19\src\Castle.Windsor\Windsor\WindsorContainer.cs: line 978
at CastleTests.TestArrayResolver.ArrayResolution_UnresolvableDependencyCausesResolutionFailure() in TestArrayResolver.cs: line 54 

I have confirmed the correct (no exceptions thrown) behavior in 2.5, and confirmed the incorrect behavior in 2.5.1, as well as trunk builds 2146 and 2137.

related questions