请注意,我更改了问题中的代码。
请参阅下面的服务器端代码(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/