ASP.NET 5 牛刀小試(二):加入第三方 DI 容器


上回介紹了 ASP.NET vNext 自帶容器的基本用法,這次要試試把 ASP.NET vNext 的自帶容器換成 Autofac。

這一次,在編寫范例程序的過程中,光是解決 KRE 與相關套件的版本不合的問題,就花了我個把小時。所以還是得先提醒一下,目前 ASP.NET vNext 還不是正式版,所以本文的操作步驟與代碼不一定符合你的開發環境。
如果你有興趣了解我最后一個碰到的問題,以及最后是怎么解決的。可以看這帖:https://github.com/aspnet/Home/issues/218(至於更早之前碰到的問題就略過不提了。不重要,因為是發生在比較早的 beta-1 版本。

本文范例所使用的開發環境:

  • Windows Server 2012 R2
  • Visual Studio 2015 Preview
  • KRE-CLR-x86.1.0.0-rc1-10798

小引

上回提到,ASP.NET vNext 的自帶容器支持四種生命周期模式:Instance、Singleton、Transient、Scoped。而且,上一次的范例程序也演示了自帶容器的基本用法。這一次要試試把 ASP.NET vNext 的自帶容器換成 Autofac。

在此之前,先補充一點基本觀念。

在 ASP.NET vNext 之前,.NET Framework 對 DI(dependency injection)的支持並不那么徹底,比較像是附加功能。到了 ASP.NET vNext ,DI(dependency injection)搖身一變,已成為一級公民。明確地說,現在不僅自帶了一個小巧的 DI 容器,同時也提供了適度的彈性,能夠與其他第三方容器銜接。不過,自帶的 DI 容器比較陽春,無法滿足某些需求,例如欲解析之類型有多個構造函數時,自帶的 DI 容器無法讓我們指定使用特定構造函數。像這類更細致的控制,還是得靠其他第三方 DI 框架才行。

ASP.NET vNext 的自帶 DI 容器

ASP.NET vNext 之所以能夠搭配其他 DI 框架一起使用,是因為它在 DI 容器實現之上提供了一個抽象層。具體來說,這個抽象層就是 System.IServiceProvider 接口,它定義了自帶 DI 容器應該具備哪些功能。.NET Framework 的其他組件(如 MVC、路由、SignalR、Entity Framework 等)都支持這個接口,而且也只會使用這個接口所定義的 DI 相關功能。所以基本上,ASP.NET vNext 的自帶 DI 容器就等於是個全局的 Service Locator。

不過,這並不代表你的應用程序也受限於此接口——你可以寫一個自定義的類型來封裝你慣用的 DI 容器,然后把自帶的 IServiceProvider 組件換掉。如此一來,所有服務解析的工作就會交給你指定的自定義容器來處理。此外,你也可以讓自定義容器只解析特定類型的服務,而把其他不需要特別處理的服務類型丟回(fallback)給自帶的容器來解析。
在 ASP.NET vNext 中,由於所有的內部框架/組件都是通過同一個容器來注冊服務,相依對象便更容易跨越框架邊界(四處流竄?),注入至以往不容易到達的地方。

自帶的 DI 容器支援下列幾種生命周期:

  • Instance:解析特定服務類型時,總是返回由你自行建立的特定對象。
  • Transient:每次解析時都建立新的對象。
  • Singleton:每次解析時都返回先前已建立的同一個對象。該對象等於是全局(對整個容器而言)共享的單一對象。
  • Scoped:針對特定范圍共享同一個對象。作用等同於特定范圍內的 Singleton。

接着要來試試把 Autofac 加入至 ASP.NET vNext 的 DI 框架。

BYOC to ASP.NET vNext

前面提過,ASP.NET vNext 允許你使用自定義容器來取代自帶的 DI 容器,但不必是完全取代,而是可以讓 ASP.NET 優先用你提供的容器,而讓自帶的容器退居二線,擔任「備援」的角色。正因為如此,我們通常不說「抽換自帶容器」,而說「把你的容器加入 ASP.NET vNext」,亦即 BYOC (Bring Your Own Container) to ASP.NET vNext。

延續上回的范例程序,原本的 Startup 類型是這么寫:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseMvc();
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        services.AddScoped<ITimeService, TimeService>(); 
    }
}

 

其中 ConfigureServices 方法使用了自帶容器來注冊 TimeService 服務。這個部分要改用 Autofac 來做。

注:ASP.NET vNext 框架會先調用 Startup 類型的 ConfigureServices 方法,然后再調用 Configure 方法。

第一步,為項目加入必要的組件引用。這個部分可直接修改 project.json,如下所示:

{
  "webroot": "wwwroot",
  "version": "1.0.0-*",
  "exclude": [
    "wwwroot"
  ],
  "packExclude": [
    "**.kproj",
    "**.user",
    "**.vspscc"
  ],
  "dependencies": {
    "Microsoft.AspNet.Server.IIS": "1.0.0.0-rc1-10790",
    "Microsoft.AspNet.Mvc": "6.0.0.0-rc1-12170",
    "Microsoft.Framework.DependencyInjection": "1.0.0.0-rc1-10655",
    "Microsoft.Framework.DependencyInjection.Autofac": "1.0.0.0-rc1-10655"
  },
  "frameworks": {
    "aspnet50": {
      "dependencies": {}
    }
  }
}

提醒:dependencies 區塊中的各組件的版本號碼可能會決定這個簡單的范例程序要花你三分鍾還是三小時才能完成。(等到 ASP.NET vNext 發布正式版本之后應該就不會有這些狀況了)

接着修改 Startup 類型的 ConfigureServices 方法,改成這樣:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    
    var builder = new ContainerBuilder();
    builder.Populate(services); 

    builder.RegisterType<TimeService>().As<ITimeService>();

    IContainer container = builder.Build();           
    return container.Resolve<IServiceProvider>();
}

上面這段代碼有幾個地方值得注意:

  • Populate 是個擴展方法,由 Microsoft.Framework.DependencyInjection.Autofac 組件提供。此擴展方法會把傳入的服務描述清單中的服務全部注冊至 Autofac 容器。
  • 注意 ConfigureServices 方法的返回類型從原先的 void 改成了 IServiceProvider。這里返回的對象實際類型會是 AutofacServiceProvider。如此一來,ASP.NET vNext 框架接收到你返回的 AutofacServiceProvider 對象之后,就會把它設定成默認的 DI 容器。 

試執行應用程序,結果應該和前一個版本相同。

(注:本文內容將會連同上一集,加以補充完善之后,更新至電子書《.NET 依賴注入》里面。)

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM