[翻譯] Autofac 中註冊的概念

来源:http://www.cnblogs.com/dongbeifeng/archive/2016/02/18/autofac-registration-concepts.html
-Advertisement-
Play Games

原文鏈接:http://docs.autofac.org/en/latest/register/registration.html 所謂註冊組件,是指創建 ContainerBuilder 的實例,並告訴它哪些組件暴露哪些服務。 組件可以用反射創建,可以提供已經創建好的對象的實例,還可以用拉姆達表達


原文鏈接:http://docs.autofac.org/en/latest/register/registration.html

所謂註冊組件,是指創建 ContainerBuilder 的實例,並告訴它哪些組件暴露哪些服務。

組件可以用反射創建,可以提供已經創建好的對象的實例,還可以用拉姆達表達式創建。ContainerBuilder 有一組 Register 方法來進行裝配。

每個組件暴露一到多個服務,這些服務用生成器的 As 方法連接起來。

// 創建生成器,生成器用來註冊組件和服務
var builder = new ContainerBuilder();

// 註冊暴露介面的類型
builder.RegisterType<ConsoleLogger>().As<ILogger>();

// 註冊已存在的對象實例
var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

// 註冊創建對象的表達式
builder.Register(c => new ConfigReader("mysection")).As<IConfigReader>();

// 生成容器,完成註冊,準備解析對象
var container = builder.Build();

// 現在可以用 Autofac 解析服務,例如,
// 這行代碼將執行拉姆達表達式解析 IConfigReader 服務
using(var scope = container.BeginLifetimeScope())
{
  var reader = container.Resolve<IConfigReader>();
}

反射組件

用類型註冊

由反射生成的組件通常按類型註冊:

var builder = new ContainerBuilder();
builder.RegisterType<ConsoleLogger>();
builder.RegisterType(typeof(ConfigReader));

使用基於反射的組件時,Autofac 選取可用參數最多的構造函數。比如,一個類有三個構造函數:

public class MyComponent
{
    public MyComponent() { /* ... */ }
    public MyComponent(ILogger logger) { /* ... */ }
    public MyComponent(ILogger logger, IConfigReader reader) { /* ... */ }
}

並使用以下代碼註冊:

var builder = new ContainerBuilder();
builder.RegisterType<MyComponent>();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
var container = builder.Build(); 

using(var scope = container.BeginLifetimeScope())
{
  var component = container.Resolve<MyComponent>();
}

解析時,Autofac 發現 ILogger 已註冊,但 IConfigReader 未註冊。於是選取第二個構造函數,因為它是可從容器獲取參數最多的構造函數。

重要說明: 通過 RegisterType 註冊的組件必須是具體類型。組件可以將抽象類和介面暴露為服務,但不能把抽象類和介面註冊為組件。Autofac 要創建組件的實例,抽象類和介面不能實例化。

指定構造函數

註冊組件時,通過使用 UsingConstructor 方法並指定構造函數的參數類型列表,可以選擇特定的構造函數,這將覆蓋自動選擇:

builder.RegisterType<MyComponent>()
       .UsingConstructor(typeof(ILogger), typeof(IConfigReader));

註意,解析時必須保證參數可用,否則將出現錯誤。參數既可以在註冊時傳遞,也可以在解析時傳遞。

實例組件

可使用 RegisterInstance 方法把預先生成的實例註冊到容器:

var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

由於 Autofac 會自動清理對象,如果想自己控制對象的生命周期,而不是由 Autofac 調用 Dispose,就需要用 ExternallyOwned 方法來註冊實例:

var output = new StringWriter();
builder.RegisterInstance(output)
       .As<TextWriter>()
       .ExternallyOwned();

將Autofac 集成到現有程式時,註冊實例是一個技巧。組件可能會用到單例模式提供的服務,與其直接引用單件,不如將單件註冊到容器:

builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();

這樣就消除了組件對單件的引用,改由容器提供。

實例暴露的預設服務是實例的具體類型,參考“服務和組件”一節。

拉姆達表達式組件

反射是創建組件的好方式。但是,如果創建邏輯超出簡單的調用構造函數時,反射就不夠用了。

Autofac 可以接受一個創建組件的委托或拉姆達表達式:

builder.Register(c => new A(c.Resolve<B>()));

參數 c 是組件上下文(IComponentContext),組件在此上下文中註冊。可以用它從容器解析出其他值,來輔助創建組件。應使用組件上下文,而不是閉包來訪問容器,這很要緊,只有這樣資源清理和嵌套容器才不會出問題。

額外的依賴可以用此上下文參數來滿足,例如, A的構造函數需要B類型的參數,並且 B 可能有其他依賴項。

表達式創建的組件暴露的預設服務是從表達式推斷出的返回類型。

以下是反射方式不能勝任,但拉姆達表達式工作良好的例子。

複雜參數

構造函數參數不能總是聲明為簡單常量。與其為使用 XML 配置語法創建特定類型的值而大傷腦筋,不如使用類似的代碼:

builder.Register(c => new UserSession(DateTime.Now.AddMinutes(25)));

(當然, session 過期時間在配置文件中更好 – 這裡只是說明個大概)

屬性註入

儘管有更好的屬性註入方式,仍然可以使用表達式和屬性初始化器來組裝屬性:

builder.Register(c => new A(){ MyB = c.ResolveOptional<B>() });

ResolveOptional 方法嘗試解析值,即使服務沒有註冊,也不會拋出異常。(如果服務已註冊但不能正確解析仍然會拋出異常) 這是解析服務的選項之一。

多數情況下不推薦屬性註入。如果組件有可選的依賴項,通過空對象模式, 重載構造函數,或構造函數參數預設值等替代方案,就可以用構造函數註入的方式來創建整潔的,“穩定的(immutable)”組件。

通過參數值選擇實現類

把組件的創建動作隔離出來後,依賴項的具體類型可以變換,這是一個很大的好處。變換通常在運行時完成,而不單是配置時:

builder.Register<CreditCard>(
  (c, p) =>
    {
      var accountId = p.Named<string>("accountId");
      if (accountId.StartsWith("9"))
      {
        return new GoldCard(accountId);
      }
      else
      {
        return new StandardCard(accountId);
      }
    });

本例,CreditCard 有兩個實現類,GoldCard 和 StandardCard,使用哪個類是在運行時由 accountId 決定的。

例子里的第二個參數名為 p,這是可選參數。

解析組件:

var card = container.Resolve<CreditCard>(new NamedParameter("accountId", "12345"));

聲明創建CreditCard 實例的委托,使用委托工廠,可以得到更整潔,類型安全的語法。

開放式泛型組件

Autofac 支持開放式泛型類型。使用 RegisterGeneric 生成器方法進行註冊:

builder.RegisterGeneric(typeof(NHibernateRepository<>))
       .As(typeof(IRepository<>))
       .InstancePerLifetimeScope();

從容器請求匹配的服務類型時,Autofac 映射到等價的閉合版本:

// Autofac 返回 NHibernateRepository<Task>
var tasks = container.Resolve<IRepository<Task>>();

註冊的特定服務類型 (如IRepository<Person>)會覆蓋開放式泛型版本。

服務和組件

註冊組件時必須告訴 Autofac 它暴露哪些服務。預設情況下,暴露的服務是組件自身的類型:

// 服務是 "CallLogger"
builder.RegisterType<CallLogger>();

組件僅可通過它暴露的服務來解析。對於本例來說:

// 沒問題
scope.Resolve<CallLogger>();

// 有問題,因為註冊時沒有將 ILogger 介面設置為組件的服務
scope.Resolve<ILogger>();

可以讓組件暴露多個服務:

builder.RegisterType<CallLogger>()
       .As<ILogger>()
       .As<ICallInterceptor>();

暴露服務後,就可以通過它解析組件。註意,將組件暴露為特定的服務後,預設服務(組件類型)會被覆蓋:

// 以下均可工作:
scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>(); 

// 但這一行不再有用,因為指定的服務覆蓋了組件類型
scope.Resolve<CallLogger>();

使用AsSelf 方法,可在暴露其他服務的同時也將自身類型暴露為服務:

builder.RegisterType<CallLogger>()
       .AsSelf()
       .As<ILogger>()
       .As<ICallInterceptor>();

這樣全部代碼都可工作:

// 註冊時暴露了合適的服務,因此都起作用
scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>();
scope.Resolve<CallLogger>();

預設註冊

如果多個組件暴露相同的服務,Autofac 將使用最後註冊的組件作為服務的預設提供程式:

builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>();

在此場景中,FileLogger 是 ILogger 的預設組件,因為它是最後註冊的。

使用 PreserveExistingDefaults 方法可以覆蓋這個行為:

builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>().PreserveExistingDefaults();

這時,ConsoleLogger 將是預設的 ILogger 因為後面註冊的 FileLogger 使用了 PreserveExistingDefaults()。

配置

可使用XML 配置或編程配置(“modules”)成組提供註冊信息,或在運行時更改註冊信息。 Autofac modules 還可實現註冊信息動態生成,或實現條件註冊邏輯。

動態註冊

Autofac modules 是引入動態註冊邏輯或簡單正交特性的最簡單方式。例如,將 log4net logger 實例動態附加到正在解析的服務。

如果需要更加動態的行為,比如添加隱式關聯類型支持,請參考check out the registration sources section in the advanced concepts area.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 裝飾模式(Decorator) 裝飾模式又名包裝(Wrapper)模式。 裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關係的一個替代方案。 裝飾模式通過創建一個包裝對象,也就是裝飾,來包裹真實的對象。 裝飾模式以對客戶端透明的方式動態地給一個對象附加上更多的責任。換言之,客戶端並不會覺得對象在
  • 1、簡介 Laravel Excel 在 Laravel 5 中集成 PHPOffice 套件中的 PHPExcel ,從而方便我們以優雅的、富有表現力的代碼實現Excel/CSV文件的導入和 導出 。 該項目的GitHub地址是: https://github.com/Maatwebsite/La
  • 什麼是設計模式 現在聊起來java設計,那就必然要聊聊設計模式,幾年前我剛入行的時候,總是聽人說起設計模式,在當時的我看來,那是多麼高大上的東西啊,以至於有種遙不可及的感覺,如今自己做java也好幾個年頭了,今天來談談如今我眼中的設計模式 說白了,設計模式就是前輩高人總結出來的一套編寫好代碼的方法,
  • 博主第一次開發商城類的項目,目前商城已上線,這裡就不打廣告了。商城的架構主要為yii2+backbone,還有一些其他blablablabla......的插件。 商城有PC端和微信端,先上線的PC端後上線微信端。 第一版的開發模式是,前端同學設計好界面原型,切好圖,做完靜態頁面交給後端人員。後端人
  • SRP單一職責原則 就一個類而言,應該僅有一個引起它變化的原因。 OCP開放封閉原則 軟體實體(類、模塊、函數等)應該是可以擴展的,但是不可修改。 LSP Liskov替換原則 子類型必須能夠替換掉它們的基類型 DIP 依賴倒置原則 抽象不應該依賴於細節。細節應該依賴於抽象。 ISP 介面隔離原則
  • 序言 Nginx的代理功能與負載均衡功能是最常被用到的,關於nginx的基本語法常識與配置已在上篇文章中有說明,這篇就開門見山,先描述一些關於代理功能的配置,再說明負載均衡詳細。 Nginx代理服務的配置說明 1、上一篇中我們在http模塊中有下麵的配置,當代理遇到狀態碼為404時,我們把404頁面
  • 前段時間,我有過一次非常有趣的談話。有個同事站出來支持Angular,他說Angular加快了Web開發的速度。我已經開發複雜的web服務超過10年了,曾經在Microsoft工作,也曾在Cyprus為Spotware工作。目前,我為矽谷的一個初創公司編寫應用程式。總的來說,我會順應潮流。但我感覺自
  • spring第二個特性是依賴註入。 學習依賴註入,首先應該明白兩個問題:1,誰依賴誰;2,誰註入,註入什麼?
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...