使用 Castle Windsor 在 Service Class 的构造函数中使用参数化构造函数初始化 class
Initialise a class with a parameterized constructor in the constructor of a Service Class using 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 注入到服务的构造函数中。但是,Greeting class 本身有一个参数化的构造函数(它需要一种语言)。在上面的代码中,我必须在服务的 Say Hello
方法中初始化问候语(使用一种语言)。如何在服务的构造函数中初始化问候语(使用语言)?
提供 运行 时间、用户驱动或其他动态依赖项的一种主要方法*是使用 factories 创建您的对象。 Castle Windsor 提供了几种不同的工具来帮助解决这个问题,或者您可以使用内核并自己实现一个工厂。
Windsor 的设施允许您提供 delegate-based 工厂,它们只是创建对象的方法。您可以在此处使用它,但您会失去创建内容的灵活性(如果您要将 ICalculator
的实现替换为其他 class,则必须更新此方法)。
为了获得最大的灵活性,您需要使用温莎的 interface-based 工厂。有了这些,您就可以提供工厂接口,然后 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());
现在,只要您想在任何地方使用计算器,就可以为它注入一个工厂,然后只需调用 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
class 重新添加到组合中,您可以使用相同的模式:
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 中讨论过。一些优点是您 可以 保护自己免受未来更改的影响并避免服务定位器模式。缺点包括接口对象的激增和工厂的潜在病毒式使用(参见我上面的第一个解决方案,我们不得不创建另一个工厂)。
* 当然还有其他的,这就是我解决这种特殊情况的方法,因为对我来说,它表明了意图,并且是代码读者最容易发现的。
根据您对 WCF 的编辑以及我所了解的您正在尝试做的事情,我会像这样实施服务合同:
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();
}
}
请注意,我已经更改了问题中的代码。
请查看下面的服务器端代码(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 注入到服务的构造函数中。但是,Greeting class 本身有一个参数化的构造函数(它需要一种语言)。在上面的代码中,我必须在服务的 Say Hello
方法中初始化问候语(使用一种语言)。如何在服务的构造函数中初始化问候语(使用语言)?
提供 运行 时间、用户驱动或其他动态依赖项的一种主要方法*是使用 factories 创建您的对象。 Castle Windsor 提供了几种不同的工具来帮助解决这个问题,或者您可以使用内核并自己实现一个工厂。
Windsor 的设施允许您提供 delegate-based 工厂,它们只是创建对象的方法。您可以在此处使用它,但您会失去创建内容的灵活性(如果您要将 ICalculator
的实现替换为其他 class,则必须更新此方法)。
为了获得最大的灵活性,您需要使用温莎的 interface-based 工厂。有了这些,您就可以提供工厂接口,然后 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());
现在,只要您想在任何地方使用计算器,就可以为它注入一个工厂,然后只需调用 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
class 重新添加到组合中,您可以使用相同的模式:
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 中讨论过。一些优点是您 可以 保护自己免受未来更改的影响并避免服务定位器模式。缺点包括接口对象的激增和工厂的潜在病毒式使用(参见我上面的第一个解决方案,我们不得不创建另一个工厂)。
* 当然还有其他的,这就是我解决这种特殊情况的方法,因为对我来说,它表明了意图,并且是代码读者最容易发现的。
根据您对 WCF 的编辑以及我所了解的您正在尝试做的事情,我会像这样实施服务合同:
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();
}
}