【问题标题】:Castle Windsor Lifestyle configuration温莎城堡生活方式配置
【发布时间】:2013-11-26 18:41:29
【问题描述】:

我花了一些时间环顾四周,似乎没有针对这种情况的明显解决方案。

我从一个程序集中注册所有类型(大约 80 种类型,接口在单独的程序集中)

    public static void RegisterAllFromAssemblies(string a)
    {
        IoC.Container.Register(
            AllTypes.FromAssemblyNamed(a)
                .Pick()
                .WithService.FirstInterface()
                .Configure(o => o.LifeStyle.PerWebRequest)
            );
    }

现在说如果我想为其中一个对象使用不同的 LifeStyle,我无法覆盖,因为我会得到 已经为给定键注册了一个组件 错误。

我已经研究了注册后改变生活方式的各种方法,但到目前为止还没有任何效果。

这里的理想解决方案应该是什么?我不想放弃 AllTypes 功能。

我想我可以在注册全部时指定一个 .Where 过滤器并跳过一些要手动注册的对象,但这不是一个非常企业化的解决方案..

【问题讨论】:

    标签: inversion-of-control castle-windsor


    【解决方案1】:

    我相信您说的是注册程序集中的所有类型,其中可能需要使用不同的生活方式注册程序集中的某些类型。所以你有 IRepository 需要是 PerWebRequest 和 ITypeMapper 可以是单例。

    我澄清一下,因为您也可能意味着您希望 IRepository 在代码中的一个位置成为 PerWebRequest,而在另一个位置成为单例。在不创建疯狂生活方式的情况下,您可以创建组件并将其注册为默认生活方式。如果您需要另一种生活方式,有时您可以创建一个新组件并从现有组件继承仅用于注册(如果这令人困惑,代码示例会显示这一点)。

    我编写了示例,以便它适用于任何一种情况,并且我提供了几种不同的方法,所有方法都集中在一次配置多个项目的过滤能力上。

    对于这个,我按类型调用特定组件的配置。它并不像您所说的那样“精力充沛”,但是如果您对该规则只有一些例外,则意图会更清楚。您会注意到您可以将配置链接在一起。除非是必需的,因为第二个配置将为第一个配置选择组件,因为我唯一的条件是服务基于 IService。这假设城堡按顺序处理配置。我相信这个假设是合理的,但有一段时间没有查看来源。

    container.Register(
        Classes.FromThisAssembly()
            .BasedOn<IService>()
            .ConfigureFor<MyComponentAsSingleton>(component => component.LifestyleSingleton())
            .Configure(component => component.LifestylePerWebRequest()).Unless(type => container.Kernel.GetAssignableHandlers(type).Count() > 0));
    

    这个使用属性来更一般地偏离正常的生活方式“PerWebRequest”

    container2.Register(
        Classes.FromThisAssembly()
            .BasedOn<IService>()
            .ConfigureIf(
                //condition to check - do we have our custom Attribute?
                registration => registration.Implementation.GetCustomAttributes(false).Any(attr => typeof(ShouldBeSingleton).IsAssignableFrom(attr.GetType())),
                //if true register as singleton
                component => component.LifestyleSingleton(),
                //else register as per web request
                component => component.LifestylePerWebRequest()
                ));
    

    现在我已经为您提供了一些解决您当前问题的示例(据我了解),让我免费为您提供建议!

    首先我不太喜欢WithService.FirstInterface()。正如智能感知所说,当您实现多个接口时,它是不确定的。任何开发人员都可以进来对类进行无害的接口更改,然后破坏系统。如果您可以使用 WithService.DefaultInterfaces() 侥幸逃脱,那么您将有一个更难搞砸的解决方案。默认接口只是告诉 Castle 在注册 Foo 组件时,如果它实现了名为 IFoo 的接口,则使用服务 IFoo。

    其次,我相信如果您将注册逻辑划分为有凝聚力的单元,您可能不会遇到这个问题。关键是要有许多实现 IWindsorInstaller 的安装程序文件。在这些安装程序中,您只需注册(使用类或类型以保持企业化)对特定安装程序有意义的类型。您在同一个安装程序中存在多种生活方式问题的可能性非常低(如果您发现这种情况,您可能需要更多安装程序)

    如果您采用这种方法,您最终可能会得到 RepositoryInstaller、ViewInstaller、ControllerInstaller 等。More on installers can be found on the castle documentation site

    如果您愿意,您可以做的是为您的所有系统创建一个通用的 boostrapper,它可以查看应用程序目录并安装该目录中的所有安装程序。由于这不是你要求的,我将停止详细说明,但如果有兴趣,你可以联系我,我可以向你展示更多关于我在说什么的信息。

    作为控制台应用程序的完整示例代码:

    using Castle.MicroKernel.Registration;
    using Castle.Windsor;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MultipleLifecyles
    {
        [AttributeUsage(AttributeTargets.Class)]
        public class ShouldBeSingleton : Attribute
        {
        }
    
        public interface IService
        {
            void DoSomething();
        }
    
        public class MyComponent : IService
        {
            public void DoSomething()
            {
                throw new NotImplementedException();
            }
        }
    
        [ShouldBeSingleton]
        public class MyComponentAsSingleton : MyComponent
        {
    
        }
    
        class Program
        {
            static void Main(string[] args)
            {
    
                //option 1
                IWindsorContainer container = new WindsorContainer();
    
                container.Register(
                    Classes.FromThisAssembly()
                        .BasedOn<IService>()
                        .ConfigureFor<MyComponentAsSingleton>(component => component.LifestyleSingleton())
                        .Configure(component => component.LifestylePerWebRequest()).Unless(type => container.Kernel.GetAssignableHandlers(type).Count() > 0));
    
                IWindsorContainer container2 = new WindsorContainer();
    
                container2.Register(
                    Classes.FromThisAssembly()
                        .BasedOn<IService>()
                        .ConfigureIf(
                            //condition to check - do we have our custom Attribute?
                            registration => registration.Implementation.GetCustomAttributes(false).Any(attr => typeof(ShouldBeSingleton).IsAssignableFrom(attr.GetType())),
                            //if true register as singleton
                            component => component.LifestyleSingleton(),
                            //else register as per web request
                            component => component.LifestylePerWebRequest()
                            ));
    
    
                Console.ReadLine();
            }
        }
    }
    

    【讨论】:

    • 欣赏精彩的文章! +1,但另一个答案实际上正是我想要的......接受,直到我看到它的问题
    【解决方案2】:

    您可以先手动注册例外吗?如果是这样,手动注册的组件不会被“AllTypes”重新注册(我建议你改用Classes)。 如果在“组”注册后手动注册组件,则会抛出异常,反之则不会。

    例如

    //YourImplementation lives in assembly 'a'
    IoC.Container.Register(
        Component.For<YourInterface>().ImplementedBy<YourImplementation>().LifestyleSingleton()
        );
    
    IoC.Container.Register(
        Classes.FromAssemblyNamed(a)
            .Pick()
            .WithService.FirstInterface()
            .Configure(o => o.LifeStyle.PerWebRequest)
        );
    

    【讨论】:

    • 将对此进行测试,但如果它有效,它实际上将是一个非常简单的解决方案,谢谢!!
    • 我验证了这行得通.. 太棒了!谁知道我所要做的就是将我的任务向上移动几行:]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多