ASP.NET MVC AutoFac Dependency Injection
2024-10-09
筆記 ASP.NET 開發中使用 AutoFac 進行依賴注入的筆記。
說明
- 透過參數的方式選擇不同的 Service 實作類別進行注入
- 批次自動註冊 Interface 與實作類別的方式
Scoped
- InstancePerDependency (transient)
- SingleInstance
- InstancePerLifetimeScope
- InstancePerRequest
快速開始
使用 ASP.NET MVC 5 需要搭配的是 Autofac.Mvc5
套件,直接在 GUI 進行搜尋與安裝即可。
接著建立一個 AutoFac\AutoFacModule.cs
用來定義注入的類別。
using Autofac;
using System.Collections.Generic;
public class AutoFacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new List<string> {}).As<IList<string>>().SingleInstance();
}
}
示範將一個 List<string>
註冊為 IList<string>
並設定為 Singleton Pattern,驗證不同的 Request 會共用相同的 Instance。
接著在 Global.asax.cs
中註冊 AutoFacModule
。
using Autofac;
using Autofac.Integration.Mvc;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterModule(new AutoFacModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
以上就完成註冊,接著就可以在 Controller 中,透過建構子注入 IList<string>
,並且作為 _stringList
的私有類別成員,在 Action 當中使用。
private readonly IList<string> _stringList;
public HomeController(IList<string> stringList)
{
_stringList = stringList;
}
public ActionResult Index()
{
_stringList.Add(new Random().Next(1, 100).ToString());
ViewBag.StringList = _stringList;
return View();
}
Instance And Class Dependency Injection
接著示範如何在 ASP.NET MVC 中進行 Service 的注入,透過建立一個 Service
介面與 Service
實作類別,並且透過 DbContext
取得資料。
public interface INorthwindService
{
IEnumerable<Product> GetProducts();
IEnumerable<Employee> GetEmployees();
}
public class NorthwindService : INorthwindService
{
private readonly NorthwindEntities _context;
public NorthwindService(NorthwindEntities context)
{
_context = context;
}
public IEnumerable<Product> GetProducts()
{
return _context.Products.ToList();
}
public IEnumerable<Employee> GetEmployees()
{
return _context.Employees.ToList();
}
}
接著在 AutoFacModule
中註冊 IService
與 Service
。
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<NorthwindEntities>().AsSelf().InstancePerDependency();
builder.RegisterType<NorthwindService>().As<INorthwindService>().SingleInstance();
}
接著在 Controller 中透過建構子注入 IService
,並且在 Action 中使用。
private readonly INorthwindService northwindService;
public HomeController(INorthwindService northwindService)
{
_service = service;
}
public ActionResult About()
{
return View(_northwindService.GetEmployees());
}
以上的設定方式如同下圖所示:
因為 Service
是 Singleton Pattern,而 dbContext
是 InstancePerDependency,所以每次 Request 都會建立新的 dbContext
重複向資料庫取得資料。
我們可以加上快取機制,在快取時間內不重複向資料庫取得資料。
public class NorthwindService : INorthwindService
{
private readonly NorthwindEntities _context;
private DateTime _lastUpdate;
private IEnumerable<Employee> _cacheEmployees;
public NorthwindService(NorthwindEntities context)
{
_context = context;
}
public IEnumerable<Employee> GetEmployees()
{
if (_cacheEmployees == null || DateTime.Now.Subtract(_lastUpdate).TotalSeconds > 10)
{
_cacheEmployees = _context.Employees.ToList();
_lastUpdate = DateTime.Now;
}
return _cacheEmployees;
}
}
批次自動註冊
builder.RegisterAssemblyTypes(typeof(WebApplication).Assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
注入 HttpContext 相關服務
在 global.asax.cs
中加入 builder.RegisterModule<AutofacWebTypesModule>
:
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
// 註冊 HttpContextBase
builder.RegisterModule<AutofacWebTypesModule>();
builder.RegisterModule(new AutoFacModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
相較於以往要透過 HttpContext.Current
取得 HttpContext
,透過 AutoFac 可以直接透過建構子注入 HttpContextBase
。
步驟多了,但帶來的好處是單元測試時可以透過 Mock 的方式注入 HttpContextBase
,同時也可以控管 HttpContext
的生命週期與解耦 Service 對於 HttpContext
的依賴。
// 以往的方式
HttpContext httpContext = HttpContext.Current;
// AutoFac 注入方式
private readonly HttpContextBase _httpContext;
public HomeController(HttpContextBase httpContext)
{
_httpContext = httpContext;
}