.Net Core中使用Autofac替換自帶的DI容器的示例
為什么叫淺談呢?就是字面上的意思,講得比較淺,又不是不能用(這樣是不對(duì)的)?。?!
Aufofac大家都不陌生了,說是.Net生態(tài)下最優(yōu)秀的IOC框架那是一點(diǎn)都過分。用的人多了,使用教程也十分豐富,官網(wǎng)教程也比較詳細(xì)(如果英文功底還不錯(cuò)的話)。
那我為什么還要寫這樣一篇博客呢,一是用作學(xué)習(xí)筆記,二就是閑的。
廢話不多說,開始正文
項(xiàng)目創(chuàng)建
云創(chuàng)建一個(gè).Net Core Api項(xiàng)目,然后再添加一個(gè)類庫,大概就是下面這樣的結(jié)構(gòu):

新建一個(gè)類庫項(xiàng)目,分別添加一個(gè)接口文件與類文件:

就這樣,我們的演示方案就搭建完成了,下面就到了演示階段。
方案演示
原始方案
俗話說的好,沒有對(duì)象 new 一個(gè)就對(duì)了:
[HttpGet]
public string Original()
{
IUserService userService = new UserService();
return userService.GetName("Original");
}
結(jié)果當(dāng)然是沒問題的:

.Net Core自帶DI
微軟給我們提供的 DI 解決方案。如果是小項(xiàng)目,需要注入的服務(wù)不多,簡(jiǎn)直無敵好用,缺點(diǎn)就是不能批量注入,下面我們來復(fù)習(xí)一下:
先在 Startup 里面的 ConfigureServices 方法內(nèi)注入(默認(rèn)且只能構(gòu)造函數(shù)注入)
services.AddScoped<IUserService, UserService>();
然后在控制器中拿到剛才注入的服務(wù):
public class DefaultController : ControllerBase
{
private readonly IUserService userService;
public DefaultController(IUserService _userService)
{
this.userService = _userService;
}
[HttpGet]
public string CoreDI()
{
return userService.GetName("CoreDI");
}
}
很顯然,一點(diǎn)問題都沒有:

Autofac
注意事項(xiàng)說在前面:
在 .Net Core2 中一般是把Startup的ConfigureServices方法返回值類型改為IServiceProvider,然后通過構(gòu)建Autofac容器并注入服務(wù)后返回。
在 .Net Core3.0之后,集成方式做了部分調(diào)整
下面演示的版本是.Net Core 3.1,也就是調(diào)整后的版本。
1、先引用 Autofac 的包,看看這下載次數(shù)

2、在 Program 中改用 Autofac 來實(shí)現(xiàn)依賴注入
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
// 就是這句
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
3、添加我們自定義的 Autofac 注冊(cè)類,并注冊(cè)我們需要的服務(wù)(默認(rèn)構(gòu)造函數(shù)注入,支持屬性注入)
public class AutofacModuleRegister : Autofac.Module
{
//重寫Autofac管道Load方法,在這里注冊(cè)注入
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<UserService>().As<IUserService>();
}
}
4、在 Startup 類中添加方法:ConfigureContainer,
public void ConfigureContainer(ContainerBuilder builder)
{
// 直接用Autofac注冊(cè)我們自定義的
builder.RegisterModule(new AutofacModuleRegister());
}
5、大功告成,控制器內(nèi)的方法甚至不用去改
public class DefaultController : ControllerBase
{
private readonly IUserService userService;
public DefaultController(IUserService _userService)
{
this.userService = _userService;
}
[HttpGet]
public string Autofac()
{
return userService.GetName("Autofac");
}
}

演示到這里就結(jié)束了,是不是感覺 Autofac 比自帶的 DI 還要麻煩。其實(shí)不然,下面我們就來看看 Autofac 對(duì)比自帶 DI 的一些特有特性。
不同的特性
批量注入
之前的項(xiàng)目我們有了用戶 UserService,需求更新,加入了商品(ProductService),有了商品那又怎么能少得了訂單(OrderService),那后面是不是還得有售后、物流、倉庫、營(yíng)銷......
如果是.Net Core 自帶的注入框架,那就只能不停的:
services.AddScoped<IProductService, ProductService>(); services.AddScoped<IOrderService, OrderService>(); ......
這時(shí)候,Autofac 的好處就體現(xiàn)出來了:批量注入。
我們先回到上面的:AutofacModuleRegister 類,加入下面這段代碼:
// 服務(wù)項(xiàng)目程序集
Assembly service = Assembly.Load("XXX.Service");
// 服務(wù)接口項(xiàng)目程序集
Assembly iservice = Assembly.Load("XXX.IService");
builder.RegisterAssemblyTypes(service, iservice)
.Where(t => t.FullName.EndsWith("Service") && !t.IsAbstract)
.InstancePerLifetimeScope()
.AsImplementedInterfaces();
上面的代碼就是批量注入 XXX.Service 與 XXX.IService 項(xiàng)目下的服務(wù)與接口。
注意:如果需要注入的服務(wù)沒有 interfac ,那么builder.RegisterAssemblyTypes 就只需要傳一個(gè)程序集就OK了。如果服務(wù)與接口同在一個(gè)項(xiàng)目,那也是要傳兩個(gè)程序集的哦。
然后我們?cè)诳刂破魅ネㄟ^構(gòu)造函數(shù)獲取注入的實(shí)例:
private readonly IUserService userService;
private readonly IProductService productService;
public DefaultController(IUserService _userService, IProductService _productService)
{
this.userService = _userService;
this.productService = _productService;
}
再對(duì)之前的 Autofac 接口添油加醋:
[HttpGet]
public string Autofac()
{
var name = userService.GetName("Autofac");
return productService.Buy(name, "批量注入");
}
結(jié)果自然是沒有問題的,如果后續(xù)需要加入其它服務(wù)都不用再單獨(dú)注入了,是不是優(yōu)點(diǎn)就體現(xiàn)出來了。批量注入還有一些其它的玩法,比如篩選類名,篩選父類等。

屬性注入
.Net Core 自帶的 DI 框架與 Autofac 默認(rèn)都是構(gòu)造函數(shù)注入,官方建議也是構(gòu)造函數(shù)注入。
但是有些同學(xué)可能就不喜歡構(gòu)造函數(shù)注入,再加上有些場(chǎng)景確實(shí)不適合構(gòu)造函數(shù)注入(比如基類實(shí)體),所以 Autofac 也支持屬性注入,下面我們來看看使用方法,在之前批量注入的基礎(chǔ)上,我們簡(jiǎn)單改造一下:
Assembly service = Assembly.Load("Autofac.Service");
Assembly iservice = Assembly.Load("Autofac.Service");
builder.RegisterAssemblyTypes(service, iservice)
.Where(t => t.FullName.EndsWith("Service") && !t.IsAbstract)
.InstancePerLifetimeScope()
.AsImplementedInterfaces()
.PropertiesAutowired(); // 屬性注入
對(duì)比構(gòu)造函數(shù)注入,屬性注入就多追加了PropertiesAutowired() 函數(shù),控制器內(nèi)修改:
public IUserService userService { get; set; }
public IProductService productService { get; set; }
注意:屬性注入記得將屬性的訪問修飾符改為注冊(cè)類可訪問的修飾符,否則會(huì)注入失敗。
下面我們來看看使用效果:

咦,怎么會(huì)空引用呢?原因大概就是 Controller 是由Mvc 模塊管理的,不在 IOC 容器內(nèi),所以在 Controller 中無法使用 Autofac 注入的實(shí)例。
那為什么構(gòu)造函數(shù)注入的時(shí)候又可以呢?大概或許可能他們都是構(gòu)造函數(shù)注入吧...
為什么是大概呢?因?yàn)槲視簳r(shí)也沒有具體去深入研究到底是什么原因?qū)е碌?,如果有一天,我想起來去研究了并且有結(jié)果了,我會(huì)在這里補(bǔ)上。
我們先解決上面的問題先,在 Startup 的 ConfigureServices 方法底部加入如下代碼:
// 使用 ServiceBasedControllerActivator 替換 DefaultControllerActivator; // Controller 默認(rèn)是由 Mvc 模塊管理的,不在 Ioc 容器中。替換之后,將放在 Ioc 容器中。 services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
然后回到我們的AutofacModuleRegister 注入 Controller:
builder.RegisterTypes(GetAssemblyTypes<Startup>(type => typeof(ControllerBase).IsAssignableFrom(type)))
.PropertiesAutowired();
這樣處理完后,屬性注入就Ok了。

存儲(chǔ)并提取容器實(shí)例
我們?cè)谥绊?xiàng)目的基礎(chǔ)上添加兩個(gè)項(xiàng)目 Common 與 Entities,存放公共類與實(shí)體類。
我們需要在實(shí)體類里面使用到 Common 項(xiàng)目中的某個(gè)類,結(jié)構(gòu)如下:
// 基類實(shí)體 public class BaseEntity
{
public Class1 common_Class1 { get; set; }
public string CreateId { get; set; }
public void Create()
{
this.CreateId = common_Class1.getCurrentUserId();
}
}
// 公共類
public class Class1
{
public string getCurrentUserId()
{
return Guid.NewGuid().ToString();
}
}
從上面的接口中我們可以看到,我需要將 Class1 通過屬性注入到容器中:
builder.RegisterType<Class1>().PropertiesAutowired().InstancePerLifetimeScope();
我們先在 Controller 中看看效果:
public Class1 class1 { get; set; }
[HttpGet]
public string Autofac()
{
return class1.getCurrentUserId();
}
很顯然結(jié)果是沒問題的:

那我們?cè)俚?BaseEntity 中去試試看:


咦,又出現(xiàn)空引用,注入失敗了。其實(shí)這個(gè)問題很明顯,我們使用的是 new 來實(shí)例化的 BaseEntity對(duì)象,沒有遵循容器實(shí)例使用規(guī)則,自然就無法使用容器中的實(shí)例了。
大家可以自己試一下,將 new 改為屬性注入就沒問題了,但是這種方案并不友好,下面要說的是另一種方案。
我們?cè)傩绿砑右粋€(gè)公共類:ContainerHelper,并聲明一個(gè)屬性用來存儲(chǔ)容器的實(shí)例:
public static class ContainerHelper
{
public static ILifetimeScope ContainerBuilder { get; set; }
}
然后回到 Startup 中,在Configure 方法的底部加入如下代碼:
ContainerHelper.ContainerBuilder = app.ApplicationServices.CreateScope().ServiceProvider.GetAutofacRoot();
再回到實(shí)體類中去使用:
public void Create()
{
if (common_Class1 == null)
{
using (var scope = ContainerHelper.ContainerBuilder.BeginLifetimeScope())
{
common_Class1 = scope.Resolve<Class1>();
}
}
this.CreateId = common_Class1.getCurrentUserId();
}


Autofac 的替換方案暫時(shí)就寫到這里了,后續(xù)如果有新的理解或心得會(huì)再做修改,淺談嘛就真的是淺談,有錯(cuò)誤或補(bǔ)充的地方請(qǐng)大家不吝賜教。
源碼這里就不提供了,大家有耐心的可以跟著手敲一遍,雖然對(duì)理解沒啥作用,但能使記憶更深刻一點(diǎn)。
到此這篇關(guān)于.Net Core中使用Autofac替換自帶的DI容器的示例的文章就介紹到這了,更多相關(guān).Net Core Autofac替換DI容器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
.NET數(shù)組使用中的注意事項(xiàng)小結(jié)
這篇文章主要介紹了.NET數(shù)組使用中的注意事項(xiàng),總結(jié)了常見的三個(gè)數(shù)組使用中的注意事項(xiàng),對(duì)于.NET初學(xué)者有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12
asp.net下將頁面內(nèi)容導(dǎo)入到word模板中的方法
asp.net下將頁面內(nèi)容導(dǎo)入到word模板中的方法,需要的朋友可以參考下。2010-10-10
.Net Winform 實(shí)現(xiàn)CSS3.0 潑墨畫效果(示例代碼)
這篇文章主要介紹了.Net Winform 實(shí)現(xiàn)CSS3.0 潑墨畫效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2024-12-12
asp.net c#采集需要登錄頁面的實(shí)現(xiàn)原理及代碼
當(dāng)我們采集頁面的時(shí)候,如果被采集的網(wǎng)站需要登錄才能采集,原理搞清楚了,就好辦了,我們所要做的僅僅是在采集的時(shí)候(或者說HttpWebRequest提交數(shù)據(jù)的時(shí)候),將Cookie信息放入Http請(qǐng)求頭里面就可以了,感興趣的朋友可以了解下,或許對(duì)你有所幫助2013-02-02
vs2017軟鏈接失效而導(dǎo)致無法進(jìn)入安裝界面的解決方法
這篇文章主要為大家詳細(xì)介紹了vs2017軟鏈接失效而導(dǎo)致無法進(jìn)入安裝界面的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-09-09
.NET的強(qiáng)類型字符串(Strongly typed string)詳解
強(qiáng)類型字符串是一種編譯時(shí)類型檢查的字符串表示方法,類似于枚舉,但具有更好的擴(kuò)展性和約束性,強(qiáng)類型字符串可以提供更好的可讀性和維護(hù)性,并且可以避免運(yùn)行時(shí)錯(cuò)誤,本文介紹什么是.NET的強(qiáng)類型字符串(Strongly typed string),感興趣的朋友一起看看吧2024-12-12
Asp.net MVC中Razor常見的問題與解決方法總結(jié)
這篇文章主要給大家介紹了關(guān)于Asp.net MVC中Razor常見的問題與解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08

