ASP.NET Core 中的依賴注入

什麼是依賴注入

軟件設計原則中有一個依賴倒置原則(DIP),爲了更好的解耦,講究要依賴於抽象,不要依賴於具體。而控制反轉(Ioc)就是這樣的原則的其中一個實現思路, 這個思路的其中一種實現方式就是依賴注入(DI)。ide

  • 什麼是依賴:當一個類須要另外一個類協做來完成工做的時候就產生了依賴。函數

  • 什麼是注入: 注入體現的是一個IOC(控制反轉的的思想)。正轉是本身來實例化須要的依賴。反轉是類不該該本身建立它,而是應該由它的調用者傳給它。因而能夠經過構造函數等讓外界把依賴傳給類。測試

  • 爲何要反轉 爲了在業務變化的時候盡少改動代碼可能形成的問題。基於抽象添加新的實現。只須要在原來注入的地方改一下就能夠了。ui

  • 什麼是容器 容器統一管理系統中的全部依賴。容器負責兩件事情:
    • 綁定服務與實例之間的映射關係
    • 獲取實例並對實例進行管理(建立與銷燬)

    容器

ASP .NET Core 中使用依賴注入

ASP.NET Core 中的依賴注入

  • IServiceCollection 負責註冊服務,是一個IList 類型的集合。
  • IServiceProvider 負責提供實例,是由IServiceCollection的擴展方法BuildServiceProvider建立的。
  • ServiceDescriptor 單個服務描述
    • Type ServiceType: 服務的類型
    • Type ImplementationType: 實現的類型
    • ServiceLifetime Lifetime: 服務的生命週期
    • object ImplementationInstance: 實現服務的實例
    • Func<IServiceProvider, object> ImplementationFactory: 建立服務實例的工廠

註冊

ServiceCollection提供了三種註冊方法分別對應着三種實例生命週期。this

  • AddSingleton 整個應用程序生命週期之內只建立一個實例
  • AddScoped 在同一個Scope內只初始化一個實例,能夠理解爲( 每個request級別只建立一個實例,同一個http request會在一個 scope內)
  • AddTransient 每一次GetService都會建立一個新的實例

作一個簡單測試:.net

  1. 建立測試類:設計

    public interface ITest
    {
        Guid Guid { get; }
    }
    
    public class Test : ITest
    {
        public Guid Guid { get; }
    
        public Test()
        {
            Guid = Guid.NewGuid();
        }
    }
  2. 在ConfigureServices裏註冊3d

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<ITest, Test>();
    }
  3. 經過三種方法來獲取這個Test類實例, Controller和View中代碼以下code

    public class HomeController : Controller
    {
        private ITest _test;
        private ILogger<HomeController> _logger;
    
        public HomeController(ITest test, ILogger<HomeController> logger)
        {
            this._test = test;
            this._logger = logger;
        }
    
        public IActionResult Index()
        {
            //經過構造函數獲取
            var res1 = this._test;
            ViewBag.TestFromConstructor = res1;
    
            //經過HttpContext獲取
            var res2 = HttpContext.RequestServices.GetService<ITest>();
            ViewBag.TestFromContext = res2;
    
            return View();
        }
    }
    @inject ITest TestFromView
    <ul>
        <li>@ViewBag.TestFromConstructor.Guid</li>
        <li>@ViewBag.TestFromContext.Guid</li>
        <li>@TestFromView.Guid</li>
    </ul>
  4. 運行,結果以下

    03d437d6-2f18-452e-a7fd-ce62cea90381
    08b31487-b02b-4d62-bc2b-6d2026389f0c
    21a7fc13-6e7b-4590-910b-68d21a7a03d1

    說明三種方式獲取了三個不一樣的實例, 刷新一下頁面, 又變成了另外三個不一樣的值.

  5. 如今在startup文件中將原來的 services.AddTransient<ITest,Test>() 改成 services.AddScoped<ITest,Test>() , 其餘不變, 從新運行一下, 結果以下

    050fef7e-2dc3-4d7d-8733-683b54b40b0b
    050fef7e-2dc3-4d7d-8733-683b54b40b0b
    050fef7e-2dc3-4d7d-8733-683b54b40b0b

    刷新一下:

    c9e5df8d-b085-4e3a-b883-fa083ba1d136
    c9e5df8d-b085-4e3a-b883-fa083ba1d136
    c9e5df8d-b085-4e3a-b883-fa083ba1d136

    三組數字相同, 刷新一下, 又變成了另外三組同樣的值, 這說明在同一次請求裏, 獲取的實例是同一個。

    最經常使用的DBContext默認構建爲Scope實例。即能減小實例初始化的消耗,還能實現跨Service事務的功能。

  6. 再將 services.AddScoped<ITest,Test>() 改成 services.AddSingleton<ITest,Test>() , 從新運行, 此次結果是

    42ef5162-5781-427b-ac9d-a152500ed32f
    42ef5162-5781-427b-ac9d-a152500ed32f
    42ef5162-5781-427b-ac9d-a152500ed32f

    發現三組值是同樣的, 說明得到的是同一個實例, 在刷新一下頁面, 仍然是這三組值, 說明屢次請求得到的結果也是同一個實例.

生命週期

使用

  • 在Startup類ConfigureService中初始化

    public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    
            services.AddTransient<ITest, Test>();
        }

    方法中默認調用了services.AddMvc(), 是IServiceCollection的一個擴展方法 public static IMvcBuilder AddMvc(this IServiceCollection services), 做用就是向這個清單中添加了一些MVC須要的服務,例如Authorization、RazorViewEngin、DataAnnotations等。

  • Controller中使用

    private ITest _test;
    private ILogger<HomeController> _logger;
    
    public HomeController(ITest test, ILogger<HomeController> logger)
    {
        this._test = test;
        this._logger = logger;
    }
  • 經過HttpContext來獲取實例
    HttpContext下有一個RequestedService一樣能夠用來獲取實例對象,不過這種方法通常不推薦。須要添加Microsoft.Extension.DependencyInjection的using來調用這個方法的。

    HttpContext.RequestServices.GetService<ITest>()
  • View中使用
    在View中經過@inject聲明

    @inject ITest TestFromView
    
    <ul>
    <li>@TestFromView.Guid</li>
    </ul>

釋放

對於每次請求, 咱們最初配置的根IServiceProvider經過CreateScope()建立了一個新的IServiceScope, 而這個IServiceScope的ServiceProvider屬性將負責本次該次請求的服務提供, 當請求結束, 這個ServiceProvider的dispose會被調用。

在2.0中, ServiceProvider只調用由它建立的 IDisposable 類型的 Dispose。 若是將一個實例添加到容器,它將不會被釋放。
例如:

services.AddSingleton<ITest>(new Test());

替換爲其它的 Ioc 容器

能夠將默認的容器改成其餘的容器, 好比Autofac, 須要把Startup類裏面的 ConfigureService的 返回值從 void改成 IServiceProvider便可。而返回的則是一個AutofacServiceProvider。

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    // Add other framework services

    // Add Autofac
    var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterModule<DefaultModule>();
    containerBuilder.Populate(services);
    var container = containerBuilder.Build();
    return new AutofacServiceProvider(container);
}
public class DefaultModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<CharacterRepository>().As<ICharacterRepository>();
    }
}

參考

相關文章
相關標籤/搜索