1. 程式人生 > 實用技巧 >.Net Core 3.x Api學習筆記 -- IOC,使用Autofac實現依賴注入(三)

.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等,以後再慢慢補充吧! 到此結束!