使用Castle Windsor在服务类的构造函数中使用参数化构造函数初始化类 [英] Initialise a class with a parameterized constructor in the constructor of a Service Class using Castle Windsor

查看:59
本文介绍了使用Castle Windsor在服务类的构造函数中使用参数化构造函数初始化类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Please note that I have changed the code in the question.

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

Please see the server side code below (WCF Service):

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。但是,Greeting类本身具有参数化的构造函数(它需要一种Language)。在上面的代码中,我必须使用该服务的说你好 方法来初始化Greeting(使用一种语言)。如何在服务的构造函数中初始化Greeting(使用一种语言)?

The code works as I would expect i.e. Castle Windsor allows me to inject a Greeting into the constructor of the service. However, the Greeting class itself has a parameterized constructor (it requires a Language). In the code above I have to initialise the Greeting (with a language) in the Say Hello method of the service. How can I initialise the Greeting (with a language) in the constructor of the service?

推荐答案

提供运行的一种主要方法*时间,用户驱动或其他方式的动态依赖项使用的是工厂创建对象。温莎城堡(Castle Windsor)提供了几种不同的功能来帮助解决此问题,或者您可以使用内核并自己实现工厂。

One primary method* of providing run-time, user-driven, or otherwise dynamic dependencies is using factories to create your objects. Castle Windsor provides several different facilities to help with this, or you can use the kernel and implement a factory yourself.

温莎的功能允许您提供基于委托的工厂,这只是用于创建一个对象。您可以在此处使用它,但是在创建内容上会失去一些灵活性(如果要将 ICalculator 的实现替换为其他类,则必须进行更新这种方法)。

Windsor's facility allows you to provide delegate-based factories, which are just methods for creating an object. You could use that here, but you lose some flexibility in what you can create (if you were to replace the implementation of ICalculator to some other class, you'd have to update this method).

为了获得最大的灵活性,您需要使用Windsor的基于接口的工厂。使用这些,您可以提供工厂的界面,然后Windsor将自动生成该界面的实现。

For maximum flexibility, you'll want to use Windsor's interface-based factories. With these, you provide a factory's interface and then Windsor will generate an implementation of it automatically.

我们以上面的代码简化版为例。如果您只有这个对象:

Let's use a simplified version of your code above as an example. If you have just have this object:

public class Calculator : ICalculator
{
    string Operator;

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

您想通过运算符,在创建对象时,您将定义如下工厂:

And you wanted to pass operator in when you create the object, you'd define a factory like so:

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

然后将其注册到安装程序中:

Then you'd register it in your installer:

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

现在,在任何想要使用计算器的地方,都需要为它注入一个工厂,然后调用创建

Now, anywhere you wanted to use a calculator, you'd inject a factory for it, then just invoke Create:

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

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

请注意 operator 参数很重要;默认情况下(可以根据需要更改此值),Windsor会按名称匹配参数。

Note that the name of the operator parameter matters; by default (you can change this if you want), Windsor matches parameters by name.

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

If we add your CalculatorService class back into the mix, you could use the same pattern:

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 并在创建此服务的位置将对象组合在一起:

But I don't really like that because why should the service care what the operator is? That's a detail of the calculator. Instead, change the factory to just accept an ICalculator and compose the objects together where you're creating this service:

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
    }
}

使用此模式有优缺点,我在在这里我的答案中进行了介绍。您可以 保护自己免受将来的更改,并避免使用服务定位器模式。缺点包括接口对象的激增和工厂的潜在使用(请参阅上面我必须创建另一个工厂的第一个解决方案)。

There are advantages and disadvantages to use this pattern, which I go over in my answer here. Some advantages are that you can protect yourself from future changes and avoid service locator patterns. Disadvantages include a proliferation of interface objects and potentially viral usage of factories (see my first solution above where we had to create another factory).

*还有其他当然,这只是我解决这种特殊情况的方法,因为对我而言,这表明了意图,并且是您的代码读者最容易发现的。

根据您对WCF的修改以及我了解您正在尝试做的事情,我将像这样实施服务合同:

Based on your edit regarding WCF and what I understand you're trying to do, I'd implement the service contract like so:

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);
    }
}






好了,您再次更改了问题以包含另一个皱纹;现在,您要基于参数实例化其他类型。您可以并且仍然应该为此使用工厂,这就是我要解决的方法:


Well, again you've changed your question to include another wrinkle; now you want to instantiate a different type based on a parameter. You can and should still use a factory for this, and this is how I'd go about it:

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();
    }
}

这篇关于使用Castle Windsor在服务类的构造函数中使用参数化构造函数初始化类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆