如何配置 WCF 服务以使用依赖注入容器(Castle Windsor)

这个例子有两部分 - 一些用于将 Castle Windsor 添加到 WCF 服务的样板步骤,然后是一个简单,具体的例子来说明我们如何配置和使用 Windsor 的容器。

这使得这个例子有点长。如果你已经了解使用 DI 容器,那么你可能只关心样板步骤。如果使用 DI 容器是不熟悉的,那么它需要更多一点 - 看到整个工作从头到尾 - 在它有意义之前。

锅炉步骤

  1. Castle Windsor WCF 集成工具 Nuget 包添加到你的 WCF 服务应用程序。这将添加对 Castle Windsor 的引用以及专门用于 WCF 服务的一些组件。

  2. 将全局应用程序类(global.asax)添加到项目中:添加>新项> Visual C#> Web>全局应用程序类。
    必须从 Application_Start 方法调用配置容器的代码。为了保持组织有序,我们可以将所有容器配置放在一个单独的类中。我们没有。没关系。在不同的例子中,你会看到不同的做法。重要的是它是从 Application_Start 方法调用的,因为那是你的组合根 - 应用程序启动的地方。我们的想法是在应用程序启动时配置容器,然后再从不直接触摸它。它只是留在后台完成它的工作。

  3. 创建一个类来配置容器。这有两件事:

    • 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());
    }
}
  1. 在 global.asax 中从 Application_Start 调用此方法:
public class Global : System.Web.HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        Container.Configure();
    }
}
  1. 创建安装程序。这只是一个实现 IWindsorInstaller 的类。这个是空的。它没有做任何事情。我们将在几个步骤中添加到此类。当我们调用 ContainerInstance.Install(FromAssembly.This()) 时,ContainerInstance 会传递给 Install 方法,以便我们可以在容器中注册依赖项。
public class WindsorInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        // Nothing here yet!
    }
}
  1. 在你创建的任何 WCF 服务的标记中,添加此指令。这表明应用程序将使用 Windsor 的 WCF 工具来创建服务的实例,这反过来意味着在创建实例时可以注入依赖项。
Factory="Castle.Facilities.WcfIntegration.DefaultServiceHostFactory, Castle.Facilities.WcfIntegration"

这结束了用于设置服务以使用 Castle Windsor 依赖注入容器的样板步骤

但是除非我们为依赖注入配置至少一个 WCF 服务,否则该示例并不完整。其余的不是样板,只是一个简单,具体的例子。

  1. 创建一个名为 GreetingService.svc 的新 WCF 服务。
  2. 编辑标记。它应该如下所示:
<%@ ServiceHost Language="C#" Debug="true" 
    Service="WcfWindsorDocumentation.GreetingService" 
    CodeBehind="GreetingService.svc.cs"     
    Factory="Castle.Facilities.WcfIntegration.DefaultServiceHostFactory, Castle.Facilities.WcfIntegration"
 %> 
  1. 用这个代替 IGreetingService(服务合同):
[ServiceContract]
public interface IGreetingService
{
    [OperationContract]
    string GetGreeting();
}
  1. 用此代码替换 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();
    }
}
  1. 添加 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;
    }
}
  1. 现在我们拥有了所需的所有类。剩下的就是在我们的容器中注册依赖项。换句话说,我们将告诉容器需要创建哪些类,因此它知道如何构建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,你会发现虽然语法和细节各不相同,但原则上它们会做同样的事情,你很容易发现相似之处。