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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章