c# - 使用 CaSTLe Windsor 在服务类的构造函数中使用参数化构造函数初始化类

标签 c# wcf dependency-injection castle-windsor

请注意,我更改了问题中的代码。

请参阅下面的服务器端代码(WCF 服务):

using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;


namespace WcfService1
{
    public class WindsorInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(
                Component.For<IGreeting, Greeting>(),
                Component.For<IGreetingService, GreetingService>());
        }
    }

    public interface ILanguage
    {
        string SayHello();
    }

    public class Spanish : ILanguage
    {
        public string SayHello()
        {
            return "Hola";
        }
    }

    public interface IGreeting
    {
        string SayHello();
    }

    public class Greeting: IGreeting
    {
        ILanguage Language;

        public Greeting (ILanguage language)
        {
            Language = language;
        }

        public string SayHello()
        {
            return Language.SayHello();
        }
    }

    public interface IGreetingFactory
    {
        IGreeting Create(ILanguage Language);
    }

    [ServiceContract]
    public interface IGreetingService
    {
        [OperationContract]
        string SayHello(string strLanguage);
    }

    public class GreetingService : IGreetingService
    {
        private readonly IGreetingFactory greetingFactory;
        private IGreeting greeting;

        public GreetingService()
        {
        }

        public GreetingService(IGreetingFactory greetingFactory)
        {
            // store the factory until we need it
            this.greetingFactory = greetingFactory;
        }

        public string SayHello (string strLanguage)
        {
            if (strLanguage == "S")
            {
                ILanguage Language = new Spanish();
                Language = new Spanish();
                greeting = new Greeting(Language);
            }
            return greeting.SayHello();
        }

    }
}

以及下面的客户端代码:

ServiceReference1.GreetingServiceClient s1 = new ServiceReference1.GreetingServiceClient();
            string greeting = s1.SayHello("S");

ServiceReference1.GreetingServiceClient 是服务引用。

代码按照我的预期工作,即 CaSTLe Windsor 允许我将 Greeting 注入(inject)到服务的构造函数中。然而,Greeting 类本身有一个参数化构造函数(它需要一个语言)。在上面的代码中,我必须在服务的 Say Hello 方法中初始化问候语(使用语言)。如何在服务的构造函数中初始化问候语(使用语言)?

最佳答案

提供运行时、用户驱动或其他动态依赖关系的一个主要方法*是使用 factories创建您的对象。 CaSTLe Windsor 提供了几种不同的工具来帮助解决此问题,或者您可以使用内核并自己实现工厂。

温莎的设施允许您提供delegate-based工厂,只是创建对象的方法。您可以在这里使用它,但是您在创建内容方面失去了一些灵活性(如果您要将 ICalculator 的实现替换为其他类,则必须更新此方法)。

为了获得最大的灵活性,您需要使用 Windsor 的 interface-based工厂。有了这些,您提供了一个工厂的接口(interface),然后 Windsor 将自动生成它的实现。

让我们使用上面代码的简化版本作为示例。如果您刚刚拥有这个对象:

public class Calculator : ICalculator
{
    string Operator;

    public Calculator(string operator)
    {
        Operator=operator;
    }
}

并且您想在创建对象时传递operator,您可以像这样定义一个工厂:

public interface ICalculatorFactory
{
    ICalculator Create(string operator);
}

然后您可以在安装程序中注册它:

kernel.Register(Component.For<ICalulatorFactory>().AsFactory());

现在,在任何您想要使用计算器的地方,您都可以为其注入(inject)一个工厂,然后只需调用 Create:

public class CalculatorUseExample
{
    private readonly ICalculator addCalculator;
    private readonly ICalculator subCalculator;

    public CalculatorUseExample(ICalculatorFactory calculatorFactory)
    {
        addCalculator = calculatorFactory.Create("+");
        subCalculator = calculatorFactory.Create("-");
    }
}

请注意,operator 参数的名称很重要;默认情况下(如果需要,您可以更改此设置),Windsor 按名称匹配参数。

如果我们将您的 CalculatorService 类重新添加到组合中,您可以使用相同的模式:

public interface ICalculatorServiceFactory
{
    ICalculatorService Create(string operator);
}

public class CalculatorService : ICalculatorService
{
    private readonly ICalculator Calculator;

    public CalculatorService(string operator, ICalculatorFactory calculatorFactory)
    {
        Calculator=calculatorFactory.Create(operator);   
    }
}

但我真的不喜欢这样,因为为什么服务要关心运营商是什么?这是计算器的详细信息。相反,将工厂更改为仅接受 ICalculator 并将创建此服务的对象组合在一起:

public interface ICalculatorServiceFactory
{
    ICalculatorService Create(ICalculator calculator);
}

public class CalculatorService : ICalculatorService
{
    private readonly ICalculator Calculator;

    public CalculatorService(ICalculator calculator)
    {
        Calculator=calculator;
    }
}

public class CalculatorServiceUseExample
{
    public CalculatorServiceUseExample(ICalculatorServiceFactory calculatorServiceFactory, ICalculatorFactory calculatorFactory)
    {
        var addCalculator = calculatorFactory.Create("+");
        var service = calculatorServiceFactory.Create(addCalculator);

        // TODO: use the service
    }
}

使用这种模式有优点和缺点,我在 my answer here 中对此进行了介绍。 。一些优点是您可以保护自己免受 future 变化的影响并避免服务定位器模式。缺点包括接口(interface)对象的激增和工厂的潜在病毒式使用(请参阅上面我的第一个解决方案,我们必须创建另一个工厂)。

* 当然还有其他方法,这只是我解决这个特定情况的方法,因为对我来说,它表明了意图,并且对于代码的读者来说是最容易发现的。


根据您对 WCF 的编辑以及我了解您正在尝试执行的操作,我将像这样实现服务契约(Contract):

public class CalculatorService : ICalculatorService
{
    private readonly ICalculatorFactory calculatorFactory;
    private ICalculator calculator;

    public CalculatorService(ICalculatorFactory calculatorFactory)
    {
        // store the factory until we need it
        this.calculatorFactory = calculatorFactory;
    }

    public void ChangeCalculatorServiceClient(string operator)
    {
        // A new operator, we'll need a new calculator
        calculator = calculatorFactory.Create(operator);
    }
}

好吧,你又改变了你的问题,加入了另一个问题;现在您想根据参数实例化不同的类型。您可以而且应该仍然使用工厂来实现此目的,这就是我的做法:

using Castle.Facilities.TypedFactory;

public class WindsorInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component.For<IGreeting, Greeting>(),
            Component.For<IGreetingFactory>().AsFactory(),
            Component.For<IGreetingService, GreetingService>(),
            Component.For<ILanguageFactory, LanguageFactory>());
    }
}

public interface ILanguageFactory
{
    ILanguage Create(string language);
}

public class LanguageFactory : ILanguageFactory
{
    private readonly IKernel kernel;

    public LanguageFactory(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public ILanguage Create(string language)
    {
        switch (language)
        {
            case "S":
                return kernel.Resolve<Spanish>();
            default:
                throw new ArgumentException();
        }
    }
}

public class GreetingService : IGreetingService
{
    private readonly IGreetingFactory greetingFactory;
    private readonly ILanguageFactory languageFactory;
    private IGreeting greeting;

    public GreetingService(IGreetingFactory greetingFactory, ILanguageFactory languageFactory)
    {
        // store the factory until we need it
        this.greetingFactory = greetingFactory;
    }

    public string SayHello (string strLanguage)
    {
        var language = languageFactory.Create(strLanguage);
        greeting = greetingFactory.Create(language);
        return greeting.SayHello();
    }
}

关于c# - 使用 CaSTLe Windsor 在服务类的构造函数中使用参数化构造函数初始化类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44436494/

相关文章:

c# - Fluent NHibernate 用 int Identity 插入表

C# Visio Interop,未调用事件处理程序

c# - 在 C# 中将名字和姓氏与全名字符串分开

c# - 启用 HTTPS、IIS 托管的 WCF 服务,如何保护它?

html - 返回 iCal 的 WCF 服务

oop - 什么是依赖注入(inject)的好比喻?

c# - 简单注入(inject)器有条件地向代表注册

angular - 如何为动态 Angular 组件做依赖注入(inject)

C# 从远程系统拖放

wcf - 将 .NET 4 WCF 服务定位为 64 位的问题