.Net Core 3.x Api學習筆記 -- IOC,使用Autofac實現依賴注入(三)
本節演示在 .net Core ApI專案中引入 Autofac 容器
專案前提條件:
.net Core ApI專案
服務層--Service層
倉儲層--Repository層
。。
第一步:安裝 NuGet 相關包,安裝如下兩個Autofac 包即可,目前版本 Autofac 6.0
第二步: 註冊
Program.cs 檔案中需要加入一句話:.UseServiceProviderFactory(new AutofacServiceProviderFactory())
/// <summary> /// 預設初始化系統內建的配置 /// </summary>/// <param name="args"></param> /// <returns></returns> public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //autofac 依賴注入.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
Startup.cs 檔案中加入如下方法,固定寫法,方法內容暫時先空著,下邊繼續。。。
/// <summary> /// Autofac引用 /// </summary> /// <param name="builder"></param>public void ConfigureContainer(ContainerBuilder builder) { //註冊邏輯 }
到這裡,配置操作基本上結束了,接下來根據業務具體操作!
業務場景:假如有一個通知系統,具體業務就是公司老闆通過電話系統通知手下都需要幹什麼事情!
在服務層 MyShop.Services 建立一個通知介面,並且實現該介面:
public interface IMessageNotice { string Send(string arg); //傳送通知 } /// <summary> /// 實現類1 電話通知 /// </summary> public class MobileNotice : IMessageNotice { public string Send(string arg) { return $"電話通知系統,通知內容:{arg}"; } }
在 Startup.cs 檔案中註冊上邊介面,builder.RegisterType<MobileNotice>().As<IMessageNotice>()
/// <summary> /// Autofac引用 /// </summary> /// <param name="builder"></param> public void ConfigureContainer(ContainerBuilder builder) { //單個註冊 builder.RegisterType<MobileNotice>().As<IMessageNotice>(); }
在控制器Controller中注入 IMessageNotice介面
public class NoticeController : ControllerBase { private readonly IMessageNotice messageNotice; public NoticeController(IMessageNotice _messageNotice) { messageNotice = _messageNotice; } /// <summary> /// 電話通知 /// </summary> /// <returns></returns> [HttpGet] public async Task<IActionResult> MobileNotice() { string content = "下午不用上班"; var result = await messageNotice.Send(content); return Ok(result); } }
然後執行測試:
假如:現在業務需求變了,老闆說了,現在網際網路時代,電話溝通已經不是唯一的工具了,還要系統可以通過微信溝通,同時以前的電話通知也要支援!
很簡單,再寫個微信通知類,實現通知介面: IMessageNotice
1 /// <summary> 2 /// 微信通知 3 /// </summary> 4 public class WebChatNotice : IMessageNotice 5 { 6 //普通傳送 7 public string Send(string arg) 8 { 9 return $"微信通知系統,通知內容:{arg}"; 10 } 11 12 //非同步傳送 13 public Task<string> SendAsync(string arg) 14 { 15 return Task.Run(() => { return $"微信通知系統,通知內容:{arg}"; }); 16 } 17 18 }
同時在 Startup 中註冊微信介面
/// <summary> /// Autofac引用 /// </summary> /// <param name="builder"></param> public void ConfigureContainer(ContainerBuilder builder) { //註冊電話介面 builder.RegisterType<MobileNotice>().As<IMessageNotice>(); //註冊微信介面 builder.RegisterType<WebChatNotice>().As<IMessageNotice>(); }
再次執行,發現電話通知變成微信通知了!但是電話通知卻沒有了,這是因為上邊 微信介面註冊時,自動把電話介面註冊覆蓋了!
老闆的要求是同時支援電話通知和微信通知,現在明顯不符合需求,那就繼續改!
修改註冊類,使用 Named<T>方法區分不同的實現類
/// <summary> /// Autofac引用 /// </summary> /// <param name="builder"></param> public void ConfigureContainer(ContainerBuilder builder) { //註冊電話介面 builder.RegisterType<MobileNotice>().Named<IMessageNotice>("mobile"); //註冊微信介面 builder.RegisterType<WebChatNotice>().Named<IMessageNotice>("webchat"); }
然後新建個Manager資料夾,新增一個通知管理類:NoticeManager,使用Autofac內建介面ILifetimeScope 來實現介面呼叫
/// <summary> /// 通知管理類 /// </summary> public class NoticeManager { //ILifetimeScope 是Autofac內建類 private readonly ILifetimeScope messageNotice; public NoticeManager(ILifetimeScope _messageNotice) { messageNotice = _messageNotice ?? throw new ArgumentNullException(nameof(_messageNotice)); } //傳送通知 public List<string> SendNotice(string arg) { var list = new List<string>(); //使用 ResolveNamed<T>方法區分 var mobile = messageNotice.ResolveNamed<IMessageNotice>("mobile"); //電話通知 var webchat = messageNotice.ResolveNamed<IMessageNotice>("webchat"); //微信通知 list.Add(mobile.Send(arg)); //呼叫傳送電話通知 list.Add(webchat.Send(arg)); //呼叫傳送微信通知 return list; } }
同時在 Startup.cs 中註冊 NoticeManager 類,自己註冊自己,相當於new了一個例項
然後在控制器Controller裡,通過構造方法注入通知管理類 NoticeManager
private readonly NoticeManager messageNotice; public NoticeController(NoticeManager _messageNotice) { messageNotice = _messageNotice; } /// <summary> /// 通知 /// </summary> /// <returns></returns> [HttpGet] public async Task<IActionResult> MobileNotice() { string content = "下午不用來上班"; var getlist = await Task.Run(() => { return messageNotice.SendNotice(content); }); return Ok(getlist); }
現在再次執行,已經按照老闆的需求同時支援電話和微信通知了!
第三步:批量註冊
使用 RegisterAssemblyTypes 方法可以按照程式集實現批量註冊,好處就不多說了!
批量註冊和單個型別註冊不衝突,可以同時存在!
/// <summary> /// Autofac引用 /// </summary> /// <param name="builder"></param> public void ConfigureContainer(ContainerBuilder builder) { //註冊電話介面 builder.RegisterType<MobileNotice>().Named<IMessageNotice>("mobile"); //註冊微信介面 builder.RegisterType<WebChatNotice>().Named<IMessageNotice>("webchat"); //註冊管理類 自己註冊自己 //builder.RegisterType<NoticeManager>(); //批量註冊程式集 沒有介面,並且以Manager結尾的類,可以代替上邊的 NoticeManager類註冊 var myshopapibll = Assembly.Load("MyShopApi"); builder.RegisterAssemblyTypes(myshopapibll) .Where(t=>t.Name.EndsWith("Manager")); //批量註冊程式集 有介面 var basedir = AppContext.BaseDirectory; var bllservice = Path.Combine(basedir, "MyShop.Services.dll"); if (!File.Exists(bllservice)) { var msg = "MyShop.Services.dll不存在"; throw new Exception(msg); } var assemblysServices = Assembly.LoadFrom(bllservice); //Assembly.Load("MyShop.Services"); builder.RegisterAssemblyTypes(assemblysServices) //多個程式集用逗號","分割 .AsImplementedInterfaces() //批量關聯,讓所有註冊型別自動與其繼承的介面進行關聯 .InstancePerDependency(); //瞬時模式 }
批量註冊型別,當一個介面存在多個實現類時,如果不用 Named<T> 方法區分,也可以使用IEnumerable<T> 方式注入建構函式獲取例項,兩者效果相同,如下標紅部分:
public class NoticeManager { private readonly ILifetimeScope lifetimeScope; //ILifetimeScope 是Autofac內建類 private readonly IEnumerable<IMessageNotice> messageNotices; //使用IEnumerable<IMessageNotice>格式注入 public NoticeManager(ILifetimeScope _lifetimeScope, IEnumerable<IMessageNotice> _messageNotices) { lifetimeScope = _lifetimeScope ?? throw new ArgumentNullException(nameof(_lifetimeScope)); messageNotices = _messageNotices ?? throw new ArgumentNullException(nameof(_messageNotices)); } //傳送通知 public List<string> SendNotice(string arg) { var list = new List<string>(); //方式1:使用 ResolveNamed<T>方法區分 //var mobile = lifetimeScope.ResolveNamed<IMessageNotice>("mobile"); //電話通知 //var webchat = lifetimeScope.ResolveNamed<IMessageNotice>("webchat"); //微信通知 //方式2 使用IEnumerable<IMessageNotice> 獲取例項,效果和 Named<T> 註冊相同 var mobile = messageNotices.Where(t=>t.GetType()==typeof(MobileNotice)).FirstOrDefault(); //電話通知 var webchat = messageNotices.Where(t => t.GetType() == typeof(WebChatNotice)).FirstOrDefault(); //微信通知 list.Add(mobile.Send(arg)); //傳送電話通知 list.Add(webchat.Send(arg)); //傳送微信通知 return list; } }
測試結果跟上邊一樣
第四步:擴充套件
為了使程式碼顯的更整潔,可以對 Autofac註冊內容進行封裝!
新建一個擴充套件資料夾:Extensions,然後在裡邊新建一個擴充套件類:AutofacExtension 並繼承Autofac.Module,然後把 Startup.cs 中Autofac的註冊內容剪下過來!
1 public class AutofacExtension : Autofac.Module 2 { 3 //重寫Load函式 4 protected override void Load(ContainerBuilder builder) 5 { 6 //註冊電話介面 7 builder.RegisterType<MobileNotice>().Named<IMessageNotice>("mobile"); 8 9 //註冊微信介面 10 builder.RegisterType<WebChatNotice>().Named<IMessageNotice>("webchat"); 11 12 //註冊管理類 自己註冊自己 13 //builder.RegisterType<NoticeManager>(); 14 15 //批量註冊程式集 沒有介面,並且以Manager結尾的類,可以代替上邊的 NoticeManager類註冊 16 var myshopapibll = Assembly.Load("MyShopApi"); 17 builder.RegisterAssemblyTypes(myshopapibll) 18 .Where(t => t.Name.EndsWith("Manager")); 19 20 21 //批量註冊程式集 有介面 22 var basedir = AppContext.BaseDirectory; 23 var bllservice = Path.Combine(basedir, "MyShop.Services.dll"); 24 if (!File.Exists(bllservice)) 25 { 26 var msg = "MyShop.Services.dll不存在"; 27 throw new Exception(msg); 28 } 29 var assemblysServices = Assembly.LoadFrom(bllservice); //Assembly.Load("MyShop.Services"); 30 builder.RegisterAssemblyTypes(assemblysServices) //多個程式集用逗號","分割 31 .AsImplementedInterfaces() //批量關聯,讓所有註冊型別自動與其繼承的介面進行關聯 32 .InstancePerDependency(); //瞬時模式 33 } 34 }
同時修改 Startup 檔案中的內容如下:
1 /// <summary> 2 /// Autofac引用 3 /// </summary> 4 /// <param name="builder"></param> 5 public void ConfigureContainer(ContainerBuilder builder) 6 { 7 builder.RegisterModule(new AutofacExtension()); 8 }
這裡只是記錄常用的一部分,還有很多擴充套件沒有記錄,比如實現AOP等,以後再慢慢補充吧! 到此結束!