1. 程式人生 > >.net core 依賴注入, autofac 簡單使用

.net core 依賴注入, autofac 簡單使用

綜述

ASP.NET Core  支援依賴注入, 也推薦使用依賴注入. 主要作用是用來降低程式碼之間的耦合度. 

什麼是控制反轉?

控制反轉(Inversion of Control,縮寫為IoC),是面向物件程式設計中的一種設計原則,可以用來減低計算機程式碼之間的耦合度。

其中最常見的方式叫做"依賴注入"(Dependency Injection,簡稱DI),還有一種方式叫"依賴查詢"(Dependency Lookup)

什麼是依賴注入?

這個概念分開來理解

1. 依賴, 當B類裡要呼叫A類完成某個功能, 那麼就可以說是B依賴於A. 

    public class ClassA
    {
        public string FnA(string str)
        {
            return "A-" + str;
        }
    }

    public class ClassB
    {
        ClassA ca = new ClassA();
        public string FnB(string str)
        {
            var art = ca.FnA(str);
            return "B-" + art;
        }
    }

上面這種方式是很常見的, 但是並不符合依賴的原則. 依賴的原則是: 依賴於抽象,而不是具體的實現. 也就是B類不能直接依賴A類, 應該依賴A的抽象介面.

 

2. 注入. 在這裡C類不去例項化A類,而是通過其他人傳遞給我,我只用就好。簡單來說就是別人對依賴建立例項化,我自己只負責使用,這個過程可以理解為注入.

    public class ClassC
    {
        ClassA _ca;
        public ClassC(ClassA ca)
        {
            _ca = ca;
        }
        public string FnC(string str)
        {
            var art = _ca.FnA(str);
            return "C-" + art;
        }
    }

 

在上面的依賴中講過, 最好不要直接依賴實現,應該依賴抽象介面. 通過注入我們瞭解到, 我們不應該直接例項化依賴項,而應該別人建立, 我們只負責使用. 綜合一下, 我們就將原來的使用方式改為以下方式

    /// <summary>
    /// 抽象介面A
    /// </summary>
    public interface InterfaceA
    {
        string FnA(string str);
    }

    /// <summary>
    /// 抽象介面A的具體實現
    /// </summary>
    public class ClassA:InterfaceA
    {
        public string FnA(string str)
        {
            return "A-" + str;
        }
    }
    
    /// <summary>
    /// 傳統方式
    /// </summary>
    public class ClassB
    {
        ClassA ca = new ClassA();
        public string FnB(string str)
        {
            var art = ca.FnA(str);
            return "B-" + art;
        }
    }
    /// <summary>
    /// 依賴注入方式, 1.依賴於抽象介面,而不是具體實現; 2.依賴項不由我們自己建立,而只是負責使用
    /// </summary>
    public class ClassD
    {
        readonly InterfaceA _ica;
        public ClassD(InterfaceA ica)
        {
            _ica = ica;
        }
        public string FnD(string str)
        {
            var art = _ica.FnA(str);
            return "D-" + art;
        }
    }

ClassB就是我們常見的使用方式, ClassD就是我們使用依賴注入的使用方式.

如何在.net core 裡使用依賴注入?

1. 建立抽象介面(過程略)

2. 實現抽象介面(過程略)

3. 在Startup類的ConfigureServices方法中註冊服務

       public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddTransient<InterfaceA, ClassA>();
        }

4. 在Controller中使用, 通過在建構函式中獲取例項, 然後使用

    [ApiController]
    [Route("[controller]/[action]")]
    public class DemoController : ControllerBase
    {
        private readonly InterfaceA _interfaceA;
        /// <summary>
        /// 通過建構函式獲取例項
        /// </summary>
        /// <param name="interfaceA"></param>
        public DemoController(InterfaceA interfaceA)
        {
            _interfaceA = interfaceA;
        }
        public IActionResult TestGet()
        {
            var rt = _interfaceA.FnA("uuu");
            return Ok(rt);
        }
    }

結果

 

 以上就是一個簡單的.net core 使用依賴注入的例子

如何選擇服務的生命週期?

在上面的第三步, 在註冊服務的時候我們使用的AddTransient , 除此之外還有 AddScoped,AddSingleton

AddTransient 暫時
暫時生存期服務 (AddTransient) 是每次從服務容器進行請求時建立的。 這種生存期適合輕量級、 無狀態的服務。

AddScoped 範圍內
作用域生存期服務 (AddScoped) 以每個客戶端請求(連線)一次的方式建立。
注意:在中介軟體內使用有作用域的服務時,請將該服務注入至 Invoke 或 InvokeAsync 方法。 請不要通過建構函式注入進行注入,因為它會強制服務的行為與單一例項類似。 有關詳細資訊,請參閱 寫入自定義 ASP.NET Core 中介軟體。

AddSingleton 單例
單一例項生存期服務 (AddSingleton) 是在第一次請求時(或者在執行 Startup.ConfigureServices 並且使用服務註冊指定例項時)建立的。 每個後續請求都使用相同的例項。 如果應用需要單一例項行為,建議允許服務容器管理服務的生存期。 不要實現單一例項設計模式並提供使用者程式碼來管理物件在類中的生存期。
注意:從單一例項解析有作用域的服務很危險。 當處理後續請求時,它可能會導致服務處於不正確的狀態。

什麼是容器?

講到這裡可能大家會有個疑問.

依賴注入把依賴的建立交給了別人, 我們只負責使用, 那麼這個誰建立, 在哪兒建立, 怎麼管理?

這個就引發了一個新的概念--容器. 容器就是負責關係的系統所有的依賴. 在我們.net core 有一個預設的容器, 專門用於管理這些依賴.

但是預設的容器在小型專案的時候夠了,但是大型專案就不夠了.

我們通過AddTransient這些方式去註冊服務, 那麼一個專案有非常的這種服務, 每一個都註冊,在startup這個裡面就變非常臃腫負責. 這個時候我們需要替換原來的容器. 

如何更換預設容器?

在這裡我們介紹一種常用的第三方容器: AutoFac, .net core 版本使用的是3.1 其他版本會略有不同.

一. nuget安裝 Autofac.Extensions.DependencyInjection

 

 二. 修改 Program.cs 檔案

新增一句

.UseServiceProviderFactory(new Autofac.Extensions.DependencyInjection.AutofacServiceProviderFactory())//Autofac

 

 

 三. 修改 Startup.cs

新增ConfigureContainer方法

        public void ConfigureContainer(ContainerBuilder builder)
        {
            // 方式一 預設註冊
            builder.RegisterType<ClassA>().As<InterfaceA>();//註冊 類似預設容器的services.AddTransient<InterfaceA, ClassA>();
        }

上面這種方式是預設的註冊, 類似.net core 自帶的註冊. 但是並不合適我們批量註冊, 下面介紹一種掃描程式集的註冊方式

        public void ConfigureContainer(ContainerBuilder builder)
        {
            //方式二 掃描程式集, RegisterAssemblyTypes接收包含一個或多個程式集的陣列作為引數. 預設地, 程式中所有具體的類都將被註冊. 
            var asm = Assembly.Load("Service");//指定dll名稱的程式集集
            var defulatAsm = Assembly.GetExecutingAssembly();//預設執行的dll
            builder.RegisterAssemblyTypes(asm, defulatAsm)
                .PublicOnly()//僅註冊public的方法
                .Except<Service.DemoService>()//排除某個類
                .Where(t => t.Name.EndsWith("Service") || t.Name == "ClassA")//可以在這裡寫一些過濾類名規則
                .AsImplementedInterfaces();
        }

在controller裡使用的方式和以前一樣

 

參考文獻

 .net core 依賴注入

Autofac 入門