依賴注入框架 DI/IoC

作用:管理類與類之間的依賴關係,幫我們構造類、確保我們代碼的可維護性和可擴展性
ASP.Net Core 中,主要是用來管理我們對象的依賴、生命週期,負責各個組件之間的協作

ASP.NET 自帶的依賴注入框架組件包:內置

  • Microsoft.Extensions.DependencyInjection.Abstractions
  • Microsoft.Extensions.DependencyInjection

這兩個:一個是抽象包、一個是具體的實現,它們用到了一個設計模式:接口實現分離模式,即:我們的組件只需要依賴抽象它的抽象接口,而不需要它的實現;當使用的時候注入它的具體實現即可;
好處是:可以在使用時決定我們用具體哪個實現,未來我們可以去做任意的擴展替換具體的依賴注入框架的具體實現;

依賴注入框架的核心類型有:

  • IServiceCollection:負責服務的註冊
  • ServiceDescriptor:每一個服務註冊時的信息
  • IServiceProvider:具體的容器,由 ServiceCollection Build出來的
  • IServiceScope:表示一個容器的子容器的生命週期

依賴注入的生命週期

  • 單例 Singleton:只有一個對象,無論獲取多少次
  • 作用域 Scoped:在Scope的生存週期內(也就是容器的生存週期內、活着子容器的生存週期內),得到的一個單例模式,而如果容器釋放掉,那麼對應的對象也會被釋放掉;
  • 瞬時(暫時) Tranient :每次從容器中獲取的對象都是全新的對象
public interface IMySingletonService { }
public class MySingletonService: IMySingletonService
{}

public interface IMyScopeService { }
public class MyScopeService : IMyScopeService
{}

public interface IMyTransientService { }
public class MyTransientService : IMyTransientService
{}

將上面注入到容器中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IMySingletonService, MySingletonService>();
    services.AddScoped<IMyScopeService, MyScopeService>();
    services.AddTransient<IMyTransientService, MyTransientService>();

    Console.WriteLine("Startup.ConfigureServices");
    services.AddControllers();
}

在控制器中的Action中使用:

[HttpGet]
public int GetService([FromServices]IMySingletonService singleton1,        // FromServices 的作用就是從容器中獲取對象
                      [FromServices]IMySingletonService singleton2,
                      [FromServices]IMyScopeService scope1,
                      [FromServices]IMyScopeService scope2,
                      [FromServices]IMyTransientService transient1,
                      [FromServices]IMyTransientService transient2,)
{
    // 輸出對象的hash指(如果兩個對象的hash值相同,那麼就可以說這兩個對象就是同一個對象)
    Console.WriteLine($"singleton1:{singleton1.GetHashCode()}");
    Console.WriteLine($"singleton2:{singleton2.GetHashCode()}");

    Console.WriteLine($"transient1:{transient1.GetHashCode()}");
    Console.WriteLine($"transient2:{transient2.GetHashCode()}");

    Console.WriteLine($"scope1:{scope1.GetHashCode()}");
    Console.WriteLine($"scope2:{scope2.GetHashCode()}");
}

運行,再刷新一次,對比兩次輸出結果;
image

服務註冊方式

1、除了用上面提到的 泛型方式注入我們的服務 外,如下:
services.AddSingleton<IMySingletonService, MySingletonService>();

2、將實例直接注入到容器中:
services.AddSingleton<IMySingletonService>(new MySingletonService());

3、工廠的方式註冊服務:

services.AddSingleton<IMySingletonService>(serviceProvider => {
    // 因爲這裏我們可以用 serviceProvider,所以我們通過這個從容器裏面獲取多個對象,然後進行組裝,得到我們最終的實現實例
	// 即工廠可以設計得比較複雜,如:實現類依賴了我們容器裏的另外一個類的情況,或者我們期望用另一個類來包裝我原有的實現的時候就可以使用
    return new MySingletonService();
})

嘗試註冊服務(即:服務如果註冊過了,就不會再註冊了)

TryAddSingleton
服務的接口類型如果已被註冊過了,那麼就無法再註冊了;

services.AddSingleton<IMySingletonService, MySingletonService>();
services.TryAddSingleton<IMySingletonService, MySingletonServiceEx>();    // 嘗試註冊,這裏無法註冊,因爲使用 TryAddSingleton ,即:服務的接口類型已被註冊
// 注意,這裏看的是 接口 是否重複註冊,而不是看實現類是否重複

// 在控制器中:
public int GetServiceList([FromServices]IEnumerable<IMySingletonService> services){   // 獲取註冊過的所有IMySingletonService的對象
    foreach(var item in services){
		Console.WriteLine($"獲取到服務實例:{item.ToString()}:{item.GetHashCode()}");
	}
	return 1;
}

發現只有一個:
image

TryAddEnumerable
服務的接口類型即使相同,而實現類不同,那麼這個服務同樣是能被註冊的

services.AddSingleton<IMySingletonService, MySingletonService>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMySingletonService, MySingletonService>());    // 嘗試註冊,可以註冊,使用 TryAddEnumerable 後,即使服務的接口類型相同,但是其實現類不同,也能被註冊;
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMySingletonService, MySingletonServiceEx>());   // 無法註冊
// 注意,這裏看的是 實現類 是否重複註冊,而不是看服務的接口類型是否重複

移除和替換 註冊

services.RemoveAll<IMySingletonService>();

services.Replace(ServiceDescriptor.Singleton<IMySingletonService, MySingletonServiceEx>());

泛型模板 註冊
當我們需要註冊一組泛型實現的時候,我們實際上註冊的時候是不知道我們的泛型類的具體的類型入參是什麼的,依賴注入框架爲我們提供了泛型模板的註冊方式,就意味着我們可以把泛型模板註冊進去,通過一行代碼來註冊所有的泛型的具體的實現;

public interface IGenericService<T>{}

public class GenericService<T>: IGenericService<T>{
    publiic Data {get; private set;}
	public GenericService(T data){
	    this.Data = data;
	}
}

// 註冊泛型模板:
services.AddSingleton(typeof(IGenericService<>), typeof(GenericService<>));


// 控制器中的使用
public MyApiController(IOrderService orderService, IGenericService<IOrderService> genericSerivce){
	// ...
}

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