如何配置 WCF 服务以使用依赖注入容器(Castle Windsor)
这个例子有两部分 - 一些用于将 Castle Windsor 添加到 WCF 服务的样板步骤,然后是一个简单,具体的例子来说明我们如何配置和使用 Windsor 的容器。
这使得这个例子有点长。如果你已经了解使用 DI 容器,那么你可能只关心样板步骤。如果使用 DI 容器是不熟悉的,那么它需要更多一点 - 看到整个工作从头到尾 - 在它有意义之前。
锅炉步骤
-
将 Castle Windsor WCF 集成工具 Nuget 包添加到你的 WCF 服务应用程序。这将添加对 Castle Windsor 的引用以及专门用于 WCF 服务的一些组件。
-
将全局应用程序类(global.asax)添加到项目中:添加>新项> Visual C#> Web>全局应用程序类。
必须从Application_Start
方法调用配置容器的代码。为了保持组织有序,我们可以将所有容器配置放在一个单独的类中。我们没有。没关系。在不同的例子中,你会看到不同的做法。重要的是它是从Application_Start
方法调用的,因为那是你的组合根 - 应用程序启动的地方。我们的想法是在应用程序启动时配置容器,然后再从不直接触摸它。它只是留在后台完成它的工作。 -
创建一个类来配置容器。这有两件事:
ContainerInstance.AddFacility<WcfFacility>()
告诉 Windsor 的 WCF 代码在创建 WCF 服务的实例时使用此特定容器。ContainerInstance.Install(FromAssembly.This())
告诉 Windsor 扫描This
程序集(换句话说,你的 WCF 项目),寻找实现IWindsorInstaller
的类。这些类将提供说明,告诉容器如何解决依赖关系。 (我们稍后会创建一个。)
public static class Container
{
private static readonly IWindsorContainer ContainerInstance = new WindsorContainer();
public static void Configure()
{
ContainerInstance.AddFacility<WcfFacility>();
ContainerInstance.Install(FromAssembly.This());
}
}
- 在 global.asax 中从
Application_Start
调用此方法:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
Container.Configure();
}
}
- 创建安装程序。这只是一个实现
IWindsorInstaller
的类。这个是空的。它没有做任何事情。我们将在几个步骤中添加到此类。当我们调用ContainerInstance.Install(FromAssembly.This())
时,ContainerInstance
会传递给Install
方法,以便我们可以在容器中注册依赖项。
public class WindsorInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
// Nothing here yet!
}
}
- 在你创建的任何 WCF 服务的标记中,添加此指令。这表明应用程序将使用 Windsor 的 WCF 工具来创建服务的实例,这反过来意味着在创建实例时可以注入依赖项。
Factory="Castle.Facilities.WcfIntegration.DefaultServiceHostFactory, Castle.Facilities.WcfIntegration"
这结束了用于设置服务以使用 Castle Windsor 依赖注入容器的样板步骤
但是除非我们为依赖注入配置至少一个 WCF 服务,否则该示例并不完整。其余的不是样板,只是一个简单,具体的例子。
- 创建一个名为
GreetingService.svc
的新 WCF 服务。 - 编辑标记。它应该如下所示:
<%@ ServiceHost Language="C#" Debug="true"
Service="WcfWindsorDocumentation.GreetingService"
CodeBehind="GreetingService.svc.cs"
Factory="Castle.Facilities.WcfIntegration.DefaultServiceHostFactory, Castle.Facilities.WcfIntegration"
%>
- 用这个代替
IGreetingService
(服务合同):
[ServiceContract]
public interface IGreetingService
{
[OperationContract]
string GetGreeting();
}
- 用此代码替换
GreetingService
(在 GreetingService.svc.cs 中)。请注意,构造函数需要一个IGreetingProvider
的实例,我们需要注入容器。
public class GreetingService : IGreetingService
{
private readonly IGreetingProvider _greetingProvider;
public GreetingService(IGreetingProvider greetingProvider)
{
_greetingProvider = greetingProvider;
}
public string GetGreeting()
{
return _greetingProvider.GetGreeting();
}
}
- 添加
IGreetingProvider
的这个实现。它还有一些自己的依赖项,我们需要容器供应。这些类的细节并不太重要。他们只是想创造一些易于理解的东西。
public interface IGreetingProvider
{
string GetGreeting();
}
public interface IComputerNameProvider
{
string GetComputerName();
}
public class ComputerNameGreetingProvider : IGreetingProvider
{
private readonly IComputerNameProvider _computerNameProvider;
public ComputerNameGreetingProvider(IComputerNameProvider computerNameProvider)
{
_computerNameProvider = computerNameProvider;
}
public string GetGreeting()
{
return string.Concat("Hello from ", _computerNameProvider.GetComputerName());
}
}
public class EnvironmentComputerNameProvider : IComputerNameProvider
{
public string GetComputerName()
{
return System.Environment.MachineName;
}
}
- 现在我们拥有了所需的所有类。剩下的就是在我们的容器中注册依赖项。换句话说,我们将告诉容器需要创建哪些类,因此它知道如何构建
GreetingService
的实例。这段代码在我们的IWindsorInstaller
实现中(样板代码的第 5 步)。
public class WindsorInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IGreetingService, GreetingService>(),
Component.For<IGreetingProvider, ComputerNameGreetingProvider>(),
Component.For<IComputerNameProvider, EnvironmentComputerNameProvider>());
}
}
这告诉容器:
- 它负责在需要时创建
GreetingService
。 - 当它试图创造
GreetingService
时,它将需要一个IGreetingProvider
。当它需要它应该创建一个ComputerNameGreetingProvider
。 - 当它试图创建
ComputerNameGreetingProvider
时,该类需要一个IComputerNameProvider
。它应该创建一个EnvironmentComputerNameProvider
实例来满足这种需求。
如果,在创建 GreetingService
或其中一个依赖项的过程中,它遇到了我们尚未注册的依赖项的需求,它会让我们知道一个有用的异常,就像这样。
缺少依赖性。
组件 WcfWindsorDocumentation.ComputerNameGreetingProvider 依赖于 WcfWindsorDocumentation.IComputerNameProvider,无法解析。
确保依赖项在容器中作为服务正确注册,或者作为内联参数提供。
这意味着某些东西取决于 IComputerNameProvider
但是没有注册来实现这种依赖。
**这只是让球滚动。**对于真实场景,正确配置和使用依赖注入容器还有很多其他功能。此示例仅涵盖将 Windsor 添加到 WCF 服务应用程序的具体内容。如果你使用不同的容器,如 Autofac 或 Unity,你会发现虽然语法和细节各不相同,但原则上它们会做同样的事情,你很容易发现相似之处。