.Net Core 學習之路-AutoFac的使用
本文不介紹IoC和DI的概念,如果你對Ioc之前沒有了解的話,建議先去搜索一下相關的資料
這篇文章將簡單介紹一下AutoFac的基本使用以及在asp .net core中的應用
Autofac介紹
組件的三種註冊方式
- 反射
- 現成的實例(new)
- lambda表達式 (一個執行實例化對象的匿名方法)
下面是一些簡短的示例,我盡可能多的列出來一些常用的註冊方式,同時在註釋中解釋下“組件”、“服務”等一些名詞的含義
// 創建註冊組件的builder var builder = new ContainerBuilder(); //根據類型註冊組件 ConsoleLogger 暴漏服務:ILogger builder.RegisterType<ConsoleLogger>().As<ILogger>(); //根據類型註冊組件 ConsoleLogger,暴漏其實現的所有服務(接口) builder.RegisterType<ConsoleLogger>().AsImplementedInterfaces(); // 根據實例註冊組件 output 暴漏服務:TextWriter var output = new StringWriter(); builder.RegisterInstance(output).As<TextWriter>(); //表達式註冊組件,這裏我們是在構造函數時傳參->"musection" 暴漏服務:IConfigReader builder.Register(c =new ConfigReader("mysection")).As<IConfigReader>(); //表達式註冊組件,解析時傳參 var service = scope.Resolve<IConfigReader>( new NamedParameter("section", "mysection")); //反射註冊組件,直接註冊了ConsoleLogger類(必須是具體的類),如果ConsoleLogger有多個構造函數,將會取參數最多的那個構造函數進行實例化 builder.RegisterType<ConsoleLogger>(); //反射註冊組件,手動指定構造函數,這裏指定了調用 MyComponent(ILogger log,IConfigReader config)的構造函數進行註冊 builder.RegisterType<MyComponent>() .UsingConstructor(typeof(ILogger), typeof(IConfigReader)); //註冊MySingleton類中的靜態變量"Instance",ExternallyOwned()函數指定自己控制實例的生命周期,而不是由autofac自動釋放 builder.RegisterInstance(MySingleton.Instance).ExternallyOwned(); //一個組件暴漏兩個服務 builder.RegisterType<CallLogger>().As<ILogger>().As<ICallInterceptor>(); //註冊當前程序集中以“Service”結尾的類 builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces(); //註冊"MyApp.Repository"程序集中所有的類 builder.RegisterAssemblyTypes(System.Reflection.Assembly.Load("MyApp.Repository")).AsImplementedInterfaces(); //構建一個容器完成註冊 var rootcontainer = builder.Build(); //可以通過下面這種方式手動獲取IConfigReader 的實現類 //這種手動解析的方式需要 從生命周期作用域內獲取組件,以保證組件最終被釋放 //不要直接從根容器rootcontainer中解析組件,很有可能會導致內存泄漏 using(var scope = rootcontainer.BeginLifetimeScope()) { var reader = scope.Resolve<IConfigReader>(); }
如果不止一個組件暴露了相同的服務, Autofac將使用最後註冊的組件作為服務的提供方。 想要覆蓋這種行為, 在註冊代碼後使用 PreserveExistingDefaults()
方法修改
生命周期
using(var scope = rootcontainer.BeginLifetimeScope())
上面的這段代碼創建了一個生命周期作用域
生命周期作用域是可釋放的,在作用域內解析的組件一定要保證在using之內使用或者最後手動調用組件的Dispose()函數
避免被引用類的生命周期大於引用類的生命周期 :如service 引用 repository 如果repository的生命周期為單例,service的生命周期為perrequest。repository不會釋放,所以最終會造成相關的service始終無法釋放的情況(Captive Dependencies)
對於一個具體組件(類)的生命周期分為以下幾種(後面的函數是autofac對應的函數):
- 每個依賴一個實例(Instance Per Dependency) (默認) ----InstancePerDependency()
- 單一實例(Single Instance) 單例 ----SingleInstance()
- 每個生命周期作用域一個實例(Instance Per Lifetime Scope)----InstancePerLifetimeScope()
- 每個匹配的生命周期作用域一個實例(Instance Per Matching Lifetime Scope)----InstancePerMatchingLifetimeScope()
- 每個請求一個實例(Instance Per Request) asp.net web請求----InstancePerRequest()
- 每次被擁有一個實例(Instance Per Owned) ----InstancePerOwned()
如果你以前在傳統的ASP.NET MVC項目中用過autofac,需要註意一些區別:
- .net Core中需要使用
InstancePerLifetimeScope
替代之前(傳統asp.net)的InstancePerRequest
,保證每次HTTP請求只有唯一的依賴實例被創建。InstancePerRequest
請求級別已經不存在了- .net Core中Web Api與Mvc的註冊方式一樣
- .net Core中不再需要註冊控制器,控制器由.net core創建,不歸autofac管理(除了控制器的構造函數),這也解釋了為什麽不再使用
InstancePerRequest
生命周期,但是可以通過AddControllersAsServices()
函數改變,想要深入了解的可以查看:https://www.strathweb.com/2016/03/the-subtle-perils-of-controller-dependency-injection-in-asp-net-core-mvc/
AutoFac 在asp .net core中的使用
在.net core 中使用autofac還是比較簡單的,相比於傳統的asp.net web 項目,省去了很多步驟
引入nuget程序包:
- Autofac
- Autofac.Extensions.DependencyInjection
startup 中代碼:
public static IContainer AutofacContainer;
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//註冊服務進 IServiceCollection
services.AddMvc();
ContainerBuilder builder = new ContainerBuilder();
//將services中的服務填充到Autofac中.
builder.Populate(services);
//新模塊組件註冊
builder.RegisterModule<DefaultModuleRegister>();
//創建容器.
AutofacContainer = builder.Build();
//使用容器創建 AutofacServiceProvider
return new AutofacServiceProvider(AutofacContainer);
}
上面代碼調用了builder的
RegisterModule
函數,這個函數需要傳入一個TModule
的泛型,稱之為autofac的模塊模塊的功能就是把所有相關的註冊配置都放在一個類中,使代碼更易於維護和配置,下面展示了
DefaultModuleRegister
中的代碼
DefaultModuleRegister:
public class DefaultModuleRegister : Module
{
protected override void Load(ContainerBuilder builder)
{
//註冊當前程序集中以“Ser”結尾的類,暴漏類實現的所有接口,生命周期為PerLifetimeScope
builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Ser")).AsImplementedInterfaces().InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Repository")).AsImplementedInterfaces().InstancePerLifetimeScope();
//註冊所有"MyApp.Repository"程序集中的類
//builder.RegisterAssemblyTypes(System.Reflection.Assembly.Load("MyApp.Repository")).AsImplementedInterfaces();
}
}
Configure函數中可以選擇性的加上程序停止時Autofac的釋放函數:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime appLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
//程序停止調用函數
appLifetime.ApplicationStopped.Register(() => { AutofacContainer.Dispose(); });
}
Controller中代碼:
private IUserSer _user;
private IUserSer _user2;
public HomeController(IUserSer user, IUserSer user2)
{
_user = user;
_user2 = user2;
}
public IActionResult Index()
{
using (var scope = Startup.AutofacContainer.BeginLifetimeScope())
{
IConfiguration config = scope.Resolve<IConfiguration>();
IHostingEnvironment env = scope.Resolve<IHostingEnvironment>();
}
string name = _user.GetName();
string name2 = _user2.GetName();
return View();
}
可以看到,因為我們將IServiceCollection中的服務填充到了autofac中了,所以現在可以在任何位置通過AutoFac解析出來.net core默認註入的服務(IConfiguration,IHostingEnvironment等)了
正常項目使用中,我們應該將
AutofacContainer
放在一個公共的類庫中以便各個工程均可調用
.Net Core 學習之路-AutoFac的使用