兩階段設計#

Autofac 採用兩階段設計:先配置,再建構不可變的容器。

// 階段一:配置
var builder = new ContainerBuilder();
builder.RegisterType<ProductService>().As<IProductService>();

// 階段二:建構
IContainer container = builder.Build();

一旦呼叫 Build() 產生 IContainer,註冊就不能再修改。這種 Builder Pattern 確保了容器在使用階段的不可變性。

Figure 13.1: Autofac 的使用模式:先配置,再解析元件

註冊方式#

Autofac 的註冊語法與多數容器方向相反——從具體類型出發,映射到抽象介面:

// Autofac:具體類型 → 抽象介面
builder.RegisterType<SqlProductRepository>().As<IProductRepository>();

// 對比多數容器:抽象介面 → 具體類型
container.Register<IProductRepository, SqlProductRepository>();

Autofac 的 RegisterType<T>().As<TInterface>() 不使用泛型型別約束,因此無法在編譯期檢查 T 是否真的實作了 TInterface。型別不匹配的錯誤只能在執行期被發現。

Lifetime 與 Scope 管理#

Autofac 提供三種 Lifetime 選項,並嚴格要求透過 Lifetime Scope 解析物件:

Autofac 方法對應模式說明
InstancePerDependency()Transient每次解析建立新實例(預設值
SingleInstance()Singleton整個容器生命週期只有一個實例
InstancePerLifetimeScope()Scoped每個 Scope 內共享同一實例
using (var scope = container.BeginLifetimeScope())
{
    var service = scope.Resolve<IProductService>();
    // 使用 service...
}

永遠從 Lifetime Scope 解析物件,而非直接從 Root Container。 直接從 Root Container 解析會導致 Scoped 物件無法正確釋放,造成記憶體洩漏。

Figure 13.3: Autofac 的 Lifetime Scope 作為可共享元件的容器

進階註冊 API#

Autofac 提供彈性但較複雜的 API 處理特殊情境:

WithParameter:傳遞原始值#

builder.RegisterType<ConnectionStringProvider>()
    .WithParameter("connectionString", "Server=...");

Lambda 註冊:完全控制建構邏輯#

builder.Register(c =>
{
    var repo = c.Resolve<IProductRepository>();
    return new CachingProductService(repo, TimeSpan.FromMinutes(5));
}).As<IProductService>();

處理多重實作#

Last Registration Wins#

當同一個介面有多個註冊時,最後一個註冊的實作會被解析為預設值:

builder.RegisterType<SqlRepository>().As<IRepository>();
builder.RegisterType<CachingRepository>().As<IRepository>();
// 解析 IRepository 會得到 CachingRepository

集合注入:IEnumerable<T>#

若需要取得某個介面的所有實作,Autofac 自動支援 IEnumerable<T>

// 自動注入所有 IEventHandler 實作
public class EventDispatcher(IEnumerable<IEventHandler> handlers) { }

Decorator 支援#

builder.RegisterType<SqlRepository>().As<IRepository>();
builder.RegisterDecorator<CachingRepository, IRepository>();

RegisterDecorator<TDecorator, TService>() 會自動將 CachingRepository 包裹在 SqlRepository 外層。

Figure 13.5: 以 Transaction、Auditing 和 Security 等 Aspect 豐富 Command Service

Composite 支援#

builder.RegisterComposite<CompositeHandler, IEventHandler>();

RegisterComposite<TComposite, TService>() 將所有 IEventHandler 實作組合到 CompositeHandler 中。

Autofac 重點回顧
  • 兩階段設計:ContainerBuilder(可變)→ IContainer(不可變)
  • 註冊方向是「具體類型 → 抽象介面」,與多數容器相反
  • 無泛型型別約束,型別錯誤在執行期才會發現
  • 永遠從 Lifetime Scope 解析,不要直接用 Root Container
  • 內建 Decorator 和 Composite 支援