如何配置 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,你會發現雖然語法和細節各不相同,但原則上它們會做同樣的事情,你很容易發現相似之處。