Castle Windsor,覆盖常规注册的组件

时间:2015-07-23 15:50:23

标签: inversion-of-control castle-windsor castle-windsor-3

我刚开始使用Castle Windsor(3.3.0),这是我第一次使用基于会议的注册。

我想通过名称约定(IDummyService - > DummyService)尽可能多地注册:

var container = new WindsorContainer();
container.Register(Types.FromThisAssembly().Pick().WithServiceDefaultInterfaces());
container.Register(Component.For<IDummyService>().ImplementedBy<DummyService>().LifestyleSingleton()); // So long, I'm throwing here...

...但当然能够稍微改变一些刚刚注册的组件(改变生命周期管理,构造函数参数等)。

因为我想保持注册尽可能简单,所以我想避免复杂的情况。我发现的解决方案很简单 - 将自定义内容移到名称约定之上:

var container = new WindsorContainer();
container.Register(Component.For<IDummyService>().ImplementedBy<DummyService>().LifestyleSingleton()); // Do custom stuff first...
container.Register(Types.FromThisAssembly().Pick().WithServiceDefaultInterfaces()); // And convention at the end...

我现在的问题是,这是如何解决我的注册的正确方法?我可以看到温莎城堡在这个区域非常强大,我宁愿正确地解决我的任务,遗憾的是看不到真实世界的例子。

4 个答案:

答案 0 :(得分:1)

一种选择是通过实现IContributeComponentModelConstruction接口来进行自定义配置 - 为每个组件调用ProcessModel方法:

public class ExtraConfiguration : IContributeComponentModelConstruction
{
    public void ProcessModel(IKernel kernel, ComponentModel model)
    {
        if (model.Implementation == typeof(DummyService))
        {
            model.LifestyleType = LifestyleType.Singleton;
        }

        if ...
    }
}

然后,您需要在注册其他组件之前将其注册到容器中:

container.Kernel.ComponentModelBuilder.AddContributor(new ExtraConfiguration());

答案 1 :(得分:1)

您的上一个注册行是一个非常广泛的注册行,虽然它可以在一个简单的应用程序中工作,但在大多数真实世界的应用程序中可能过于简单。

通常,程序集将提供一组或多组服务。使用各种选择方法(例如InNamespace,BasedOn,Where),您可以注册每组服务并配置它们的生命周期,依赖关系,命名等。我倾向于为每组服务创建一个单独的方法。 (例如RegisterDataAccessComponents())对程序集提供的服务集非常明确,这使得以后重新访问代码并确定提供的内容以及跟踪影响运行时行为的配置变得更加容易。您仍在按惯例注册,但您正在更明确地进行注册。

为此,我发现创建负责注册和连接程序集提供的服务集的IWindsorInstaller实现也有助于将容器初始化与其他应用程序初始化任务分离。

答案 2 :(得分:1)

Castle Windsor确实是非常强大且成熟的依赖注入平台。它有许多内部扩展,实际上可以实现您的目标。请参阅here注册服务程序集内置工具。

我已经使用它近5年了,对我来说,以下是最好的方法:

        // at the startup of the application
        _container = (new WindsorContainer()
            .AddHelperFacilities() // IWindsorContainer extension that comes from Framework.InversionOfControl 
            .AddWebApiAdapter() // IWindsorContainer extension that comes from Framework.InversionOfControl.WebApi 
            .InitializeDomainUsingConventions(  // IWindsorContainer extension that comes from Framework.InversionOfControl
                AppDomain.CurrentDomain, // domain for which container will be building registrations
                "ApplicationName.*", // regext to speed up registration process by processing only services from application namespace
                new WebApiControllersRegistrationConvention(), new DefaultRegistrationConvention())); // one or more conventions 
    // DefaultRegistrationConvention() comes from Framework.InversionOfControl
    // WebApiControllersRegistrationConvention() comes from Framework.InversionOfControl.WebApi . A separate assembly to be referenced to avoid extra dependancies on Asp.NET WebApi assemblies
            .Resolve<IApplication>.Start(); // resolves application specific entry point and launches the application

然后是Framework.InversionOfControl:

namespace Framework.InversionOfControl
{
    public static class WindowsContainerExtensions
    {
        public static IWindsorContainer InitializeDomainUsingConventions(
            this IWindsorContainer container, AppDomain appDomain, string commonNamespaceDenominatorMask, params IRegistrationConvention[] registrationConventions)
        {
            var assembliesToInitialize = new List<Assembly>();
            var runtimeAssemblies = new List<Assembly> { Assembly.GetCallingAssembly() };
            var processedAssemblies = new List<Assembly>();
            runtimeAssemblies.AddRange(appDomain.GetAssemblies());
            foreach (var assembly in runtimeAssemblies)
            {
                ProcessAssembly(assembly, assembliesToInitialize, processedAssemblies, commonNamespaceDenominatorMask, commonNamespaceDenominatorMask == null);
            }
            var allRuntimeTypes = new List<Type>();
            foreach (var assembly in assembliesToInitialize)
            {
                var assemblyTypes = assembly.GetTypes().ToList();
                var installerTypes = assemblyTypes.Where(t => !t.IsInterface && !t.IsAbstract && t.GetInterfaces().Contains(typeof(IWindsorInstaller))).ToArray();
                if (installerTypes.Any())
                {
                    foreach (var installer in installerTypes.Select(installerType => (IWindsorInstaller)Activator.CreateInstance(installerType)))
                    {
                        container.Install(installer);
                    }
                }
                else
                {
                    allRuntimeTypes.AddRange(assemblyTypes);
                }
            }
            foreach (var registrationConvention in registrationConventions)
            {
                registrationConvention.RegisterTypesUsingConvention(container, allRuntimeTypes);
            }
            return container;
        }

        private static void ProcessAssembly(Assembly assembly, List<Assembly> assemblies, List<Assembly> processedAssemblies, string commonNamespaceDenominatorMask, bool fullScan)
        {
            if (processedAssemblies.Any(x => x.FullName == assembly.FullName)) return;
            if (assembly == typeof(WindowsContainerExtensions).Assembly) return;
            processedAssemblies.Add(assembly);
            var initialize = (new Regex(commonNamespaceDenominatorMask, RegexOptions.IgnoreCase)).Match(assembly.FullName).Success;
            if (initialize && assemblies.Any(x => x.FullName == assembly.FullName))
            {
                initialize = false;
            }
            if (initialize)
            {
                assemblies.Add(assembly);
            }

            foreach (var referencedAssembliyNames in assembly.GetReferencedAssemblies())
            {
                var referencedAssembliyNames1 = referencedAssembliyNames;
                if (assemblies.Any(x => x.FullName == referencedAssembliyNames1.FullName)) continue;
                if (fullScan == false && (new Regex(commonNamespaceDenominatorMask, RegexOptions.IgnoreCase)).Match(assembly.FullName).Success == false) continue;
                Assembly referencedAssembliy;
                try
                {
                    referencedAssembliy = Assembly.Load(referencedAssembliyNames);
                }
                catch 
                {
                    continue;
                }
                ProcessAssembly(referencedAssembliy, assemblies, processedAssemblies, commonNamespaceDenominatorMask, fullScan);
            }
        }

        public static IWindsorContainer AddHelperFacilities(this IWindsorContainer container)
        {
            container.AddFacility<TypedFactoryFacility>();

            container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));

            container.Register(Component.For<IWindsorContainer>().ImplementedBy<WindsorContainer>());
            container.Register(Component.For<IContainerAccessor>().ImplementedBy<ContainerAccessor>());
            container.Resolve<IContainerAccessor>().Container = container;

            return container;
        }
    }

    public interface IRegistrationConvention
    {
        IWindsorContainer RegisterTypesUsingConvention(IWindsorContainer container, List<Type> assemblyTypes);
    }

    public class DefaultRegistrationConvention : IRegistrationConvention
    {
        /// <summary>
        /// Register every service possible from the calling assembly with default singleton lifestyle
        /// with the exception of ISomething Factory where the the ISomething GetSomething() where
        /// Something that implements ISomething is registered with transient lifestyle
        /// </summary>
        public IWindsorContainer RegisterTypesUsingConvention(IWindsorContainer container, List<Type> assemblyTypes)
        {
            // Step 1: Factories installation.
            // We register interfaces ending 'Factory' keyword like proxy (implementionless) factories.
            var factoryServices = new List<Type>();
            var factorySelector = new FullNameFactorySelector();
            foreach (var factoryType in assemblyTypes.Where(t => t.Name.EndsWith("Factory") && t.IsInterface))
            {
                foreach (var method in factoryType.GetMethods())
                {
                    if (method.Name.StartsWith("Get") == false) continue;
                    if (method.ReturnType.IsInterface == false) continue;
                    factoryServices.Add(method.ReturnType);
                }

                container.Register(Component.For(factoryType).AsFactory(factorySelector));
            }

            // Step 2: Rest of the services registrations
            // transientServices list is populated with services that needs to has transient lifespan
            // everything else needs to go as preconfigured lifestyle - lifeStyleType
            var components = assemblyTypes.Where(t => !t.IsInterface && !t.IsAbstract);
            foreach (var component in components)
            {
                // for every interface and implementation do registration
                foreach (var service in component.GetInterfaces())
                {
                    IRegistration registration;
                    Type service1 = service;
                    if (factoryServices.Any(x => x.FullName == service1.FullName))
                    {
                        if (component.IsGenericType)
                        {
                            // GetInterfaces() and GetMethod().ReturnType both returns Type.FullName = null
                            // Castle.Windsor fails to Resolve later generic types if registered type is with FullName = null,
                            // Workaround is to find the type with correct FullName from the 'assemblyTypes'
                            var serviceWithFullName = assemblyTypes.FirstOrDefault(x => x.Name == service1.Name);
                            if (serviceWithFullName == null) continue; // if null then the mapping is not supported by this convention
                            registration = Component.For(serviceWithFullName)
                                .ImplementedBy(component)
                                .LifestyleTransient()
                                .Named(serviceWithFullName.FullName + " / " + component.FullName);
                        }
                        else
                        {
                            registration = Component.For(service)
                                .ImplementedBy(component)
                                .LifestyleTransient()
                                .Named(service.FullName + " / " + component.FullName);
                        }
                    }
                    else
                    {
                        registration = Component.For(service)
                            .ImplementedBy(component)
                            .Named(service.FullName + " / " + component.FullName)
                            .LifeStyle.Is(LifestyleType.Singleton);

                    }
                    container.Register(registration);
                }
            }

            return container;
        }
    }
}

以上所有内容都是Castle Windsor以模块化和可扩展的方式完成的,而不限制Castle Windsor的功能。它只需几行就可以按照约定注册整个应用程序,并允许添加特定的约定,如:Mvc,WebApi,AutoMapper,Wcf,Quartz等等

答案 3 :(得分:0)

为此目的,有一些ConfigureX方法,即ConfigureIf或基于类型的ConfigureFor<IDummyService>

Here's指向相关文档的链接。