Castle Windsor: Register class with internal const

2019-02-24 11:54发布

问题:

A Castle Windsor question: Is it possible to register a class that has an internal constructor with the container?

Thanks Joni

回答1:

Yes, it is possible. Default component activator looks only for public constructors. You can either provide custom component activator for that component that would take internal constructor into account, or use for example factory to activate the component:

var container = new WindsorContainer()
    .AddFacility<FactorySupportFacility>()
    .Register( Component.For<Foo>()
                   .UsingFactoryMethod( () => new Foo() ) );
var foo = container.Resolve<Foo>();

However you should really reconsider making the .ctor internal in the first place. It is really rarely a good idea to do so, especially when you're exposing the class as a component to the outside world anyway.



回答2:

Following the accepted answer's advice, I was able to extend the DefaultComponentActivator class to work with protected constructors (it still does not work with private or internal; the code below works fine, that is, but something else in the DynamicProxy creation chain fails).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Castle.Core;
using Castle.MicroKernel;
using Castle.MicroKernel.ComponentActivator;
using Castle.MicroKernel.Context;

namespace /* YourNameSpaceHere */
{
    [Serializable]
    public class NonPublicComponentActivator : DefaultComponentActivator
    {
        public NonPublicComponentActivator(ComponentModel model, IKernelInternal kernel, ComponentInstanceDelegate onCreation, ComponentInstanceDelegate onDestruction)
            : base(model, kernel, onCreation, onDestruction)
        { /* do nothing */ }

        private readonly List<Type> loadedTypes = new List<Type>();
        protected override ConstructorCandidate SelectEligibleConstructor(CreationContext context)
        {
            lock (loadedTypes)
            {
                if (!loadedTypes.Contains(context.RequestedType))
                {
                    loadedTypes.Add(context.RequestedType);

                    // Add the missing non-public constructors too:
                    var ctors = context.RequestedType.GetConstructors
                    (
                        BindingFlags.NonPublic | BindingFlags.Instance
                    );

                    foreach (var ctor in ctors)
                    {
                        Model.AddConstructor
                        (
                            new ConstructorCandidate
                            (
                                ctor,
                                ctor.GetParameters().Select(pi => new ConstructorDependencyModel(pi)).ToArray()
                            )
                        );
                    }
                }
            }

            return base.SelectEligibleConstructor(context);
        }
    }
}

Then, on your container you register this against a ComponentRegistration object via the generic Activator method, so my container.Register call looks something like this:

    _container.Register
    (
        // . . .

        AllClasses.FromThisAssembly().BasedOn<ISomeInterface>()
            .Configure
            (
                c => c.LifeStyle.Transient
                    .Interceptors<MyInterceptor>()
                    .Activator<NonPublicComponentActivator>() // <--- REGISTERED HERE
            ),

        // . . .
    );

Hope that helps someone!