Autofac 觀念、實務與紀錄
IoC, DI, Autofac
觀念
IoC
, Inversion of Control ,控制反轉。 一種軟體叫用(或引用)資源的行為,一般來說上層才可引用下層資源,下層一般不需要也沒必要引用到上層的資源,因為會不小心就dead lock且破壞架構階層關係(新鮮人常幹這種事)。 IoC就是反過來,可由下層引用上層一小部份資源。傳統上我們用Callback Function
(回呼函式)處理這種下對上的需求。現在可以用lamda
函式就可以很簡單地實作 IoC 的效果。DI
(Dependency Injection) 依賴注入。一種軟體設計模式,介面的實作可在事後更替。用GoF design pattern來類比就是『Bridge pattern』。
Autofac
為一種實作DI的軟體技術,並可實現IoC行為。 IoC 的實現用 Callback 或 lamda 就可以辦到,為何還要用Autofac 這麼麻煩呢?因為callback
或lamda
只能反轉函式,而Autofac
可以直接反轉整個物件。
在實際上,大部份 Autofac 只用到 DI 程度,很少用到 IoC 這個有點進階的程度。且Autofac 設計的太靈活非常容易爛用,容易造成架構各階層模組的權責混亂。若非必要請不要用,若真的需要請有限度的使用需要的那一部份就好。
一個現實狀況,DI 在 .NET5/MVC6 似手變成了標備,不用都都行啊。
紀錄
開發環境
IDE: Visual Studio 2017
.NET Fx 4.5.1
MVC 5.2.4
Autofac 4.9.4
Autofac.Mvc5 4.0.2
Autofac 常用三種注射方式.
Constructor injection (基本用法,大部份用這招就能滿足。本例有練習)
Property injection (本例有練習)
Method injection
下層
注入目標介面定義,注入介面只能在下層定義不過可以在上層實作。
namespace YourProject.Biz
{
/// 欲DI注入的目標介面
public interface IFooService
{
void ShowMeTheMoney(int money);
}
}
下層注入上層資源,一個IoC範例
namespace YourProject.Biz
{
public class MySampleBiz
{
/// <summary>
/// 將會DI注入,Property injection sample
/// </summary>
public IFooService _foo { get; set; }
public MySampleBiz()
{
/// _foo 將會被注入上層資源,不用建構它。
/// 此例是用 Property injection
/// 當然也能用 Constructor injection 或 Method injection。
}
/// 一個 IoC 練習
public void IamRich(int rich)
{
_foo.ShowMeTheMoney(rich); /// 將引用上層資源。
Debug.WriteLine($"I am rich {rich}. Yes");
}
}
}
上層
在上層實作下層介面
namespace YourProject.Models
{
/// 實作下層的介面,以實現IoC應用。
public class FooService : Biz.IFooService, IDisposable
{
public FooService() {
Debug.WriteLine($"FooService ctor.");
}
public void ShowMeTheMoney(int money) {
Debug.WriteLine($"ShowMeTheMoney {money}");
}
void IDisposable.Dispose()
{
Debug.WriteLine($"FooService dispose.");
/// dispose 未實作完整請自行補滿,因非本主題重點。
}
}
}
上層注入應用範例
namespace YourProject.Areas.Templates.Controllers
{
public class FooController : Controller
{
private Biz.IFooService _foo; // 將注入介面以使用目標物件.
private Biz.MySampleBiz _biz; // 將直接注入目標物件。
public BS3LayoutController(Biz.IFooService foo, Biz.MySampleBiz biz)
{
/// 由DI注入成介面引用目標物件,Constructor injection sample。
_foo = foo;
/// 由DI直接注入目標物件。也是 Constructor injection sample。
/// 註:一般下層資源直接建構就好沒有注入的必要。
_biz = biz;
}
public ActionResult Index()
{
_foo.ShowMeTheMoney(1000); /// 使用被注入的資源
_biz.IamRich(9999); /// 使用被注入的資源
return View();
}
}
}
Autofac 註冊程序 ※ Autofac 註冊語法中,同一目的有多種寫法,說是彈性然而也是混亂源頭之一, 請謹用。
using Autofac;
using Autofac.Integration.Mvc;
namespace YourProject.App_Start
{
public class AutofacConfig
{
public static void Bootstrapper()
{
// 容器建立者
var builder = new ContainerBuilder();
// ------ 開始註冊 ------
/// 註冊Controllers
builder.RegisterControllers(Assembly.GetExecutingAssembly());
/// 註冊Biz,註冊整個Biz模組,其中類別名稱結尾為"Biz"的類別。
/// 預設就有 Constructor injection 能力。
builder.RegisterAssemblyTypes(typeof(Biz.MySampleBiz).Assembly)
.Where(t => t.Name.EndsWith("Biz"))
.AsSelf()
.PropertiesAutowired(); // 並啟動 Property injection 能力。
/// 註冊單一類別 FooService 成介面 IFooService.
///※ 註冊上層類別 FooService 並轉型下層介面 IFooService,這樣就可以被注入到下層以實現IoC應用。
builder.RegisterType<FooService>().As<Biz.IFooService>();
/// 以下是其他註冊例子,留參用
//builder.RegisterType<DB.MyLabDbConnHelper>()
// .As<IDbConnection>()
// .InstancePerRequest();
//// 註冊Service
//builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
// .Where(t => t.Name.EndsWith("Service"))
// .AsSelf();
//// 註冊Repository
//builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
// .Where(t => t.Name.EndsWith("Repository"))
// .AsSelf();
//var services = Assembly.Load("TimidoColor.Services");
//builder.RegisterAssemblyTypes(services).AsImplementedInterfaces();
//builder.RegisterFilterProvider();
// ------ 註冊結束 ------
// 建立容器
IContainer container = builder.Build();
// 建立相依解析器,解析容器內的型別
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
}
系統一開始就需啟動註冊程序
using YourProject.App_Start;
namespace YourProject
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
/// 系統一開始就必需啟動Autofac註冊
AutofacConfig.Bootstrapper();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
(EOF)
Last updated