重新整理 .net core 實踐篇————依賴注入應用[二]


前言

這里介紹一下.net core的依賴注入框架,其中其代碼原理在我的另一個整理<<重新整理 1400篇>>中已經寫了,故而專門整理應用這一塊。

以下只是個人整理,如有問題,望請指點。

正文

為什么有這個依賴注入呢?

假設人們面臨這樣一個問題。

比如說一個人做飛機去北京。那么人和飛機有什么關系呢?人和北京有什么關系呢?

假設有3個主體類,一個是人一個是飛機一個是北京。他們之間到底應該怎么組織呢?

先說人和北京的關系,人要去北京,那么飛機就是一個交通工具。是的,只是一個交通工具,那么人和飛機就沒有直接的關系,而是人通過交通工具去了北京。

同樣北京也可以抽象處理,北京是一個地點。那么這個語句就變成了"人通過交通工具去了某個地方",這個交通工具可以是飛機可以是高鐵,未來可以是火箭。地點同樣如此。

如果抓住這個詞,“未來”,那么其擴展性就很強了,不僅現在擁有的還代表以后。就像我們的usb接口一樣,未來可能有新的設備只要適配了usb接口,那么我主機里面的東西將不用動任何東西就可以運行新的設備。

因為我的主機沒有動任何東西,那么是不是代表我的穩定性?所以依賴注入不僅可以確保我們代碼的可維護性和可擴展性,最重要的是穩定性(當然了穩定性是可維護性的一種),而代碼最重要的就是穩定性。

在asp.net core 的整個架構中,依賴注入框架提供了對象創建和生命周期管理的核心能力,各個組件相互協作,可以說是整個框架的核心了。

那么這里有一個關鍵詞生命周期,將會伴隨着整篇文章的核心。

.net core 的依賴實現主要是通過 Microsoft.Extensions.DependencyInjection;

其這個包,是實現了Microsoft.Extensions.DependencyInjection.Abstrations;

Microsoft.Extensions.DependencyInjection.Abstrations是接口的定義,Microsoft.Extensions.DependencyInjection 是接口的實現。

那么這里就可以想象到既然提供了接口,那么一般來說就有第三方實現,后面會介紹第三方實現。

依賴注入的核心由下面幾個部門組成。

  1. IServiceCollection 負責服務的注冊

  2. ServiceDescriptor 每一個服務注冊時候的信息

  3. IServiceProvider 具體的容器

  4. IServiceScope 容器的子容器的生命周期

那么什么是生命周期呢?

  1. 單例模式

  2. 作用域模式

  3. 瞬時模式

那么這個怎么理解呢?首先建立起這樣一個模型。

這個模型是什么意思呢? 首先我們創建單例模式產生的對象會存在root中,如果作用域模式那么創建的對象將會在某個作用域中,這個作用域是自己指定的。

而瞬時模式呢,就是不存儲在任何一個作用域中。

當我們通過這個依賴注入服務創建對象的時候,如果是單例模式,那么會這么干,查找root容器中有沒有該注冊信息的實例,如果有就直接返回,如果沒有那么創建存儲在root容器中。

如果是作用域模式,那么會去找當前作用域有沒有該注冊信息的實例,如果有那么直接返回,如果沒有那么存儲在當前作用域的容器中。

同樣,如果作用域模式,那么該作用域的容器消失了,里面存儲的東西就沒了,那么自然通過該作用域創建的對象就消失了。

那么就用代碼來實驗一下。

有三個類:

里面的內容就是一個接口,然后一個具體的類,這里演示其中一個。

public interface IMySingletonService
{
}
public class MySingletonService: IMySingletonService
{
}

然后再configureServices 中進行注冊:

services.AddSingleton<IMySingletonService, MySingletonService>();
services.AddScoped<IMyScopedService, MyScopedService>();
services.AddTransient<IMyTransientService, MyTransientService>();

然后寫一個http的get方法。

[HttpGet]
public int GetService([FromServices] IMySingletonService mySingleton1,
	[FromServices] IMySingletonService mySingleton2,
	[FromServices]IMyScopedService myScoped1,
	[FromServices] IMyScopedService myScoped2,
	[FromServices] IMyTransientService myTransient1,
	[FromServices] IMyTransientService myTransient2)
{
	Console.WriteLine($"singleton1:{mySingleton1.GetHashCode()}");
	Console.WriteLine($"singleton1:{mySingleton2.GetHashCode()}");
	Console.WriteLine($"myScoped1:{myScoped1.GetHashCode()}");
	Console.WriteLine($"myScoped2:{myScoped2.GetHashCode()}");
	Console.WriteLine($"myTransient1:{myTransient1.GetHashCode()}");
	Console.WriteLine($"myTransient2:{myTransient2.GetHashCode()}");
	return 1;
}

這里通過hashcode 來驗證是否兩個對象是否相等。

截圖如下:

然后我們再訪問一次這個接口。

上述可以得出,每一個http請求將會在同一個子容器中,且同一子容器獲取的注冊對象相同。而單例每次都相同。

transient 則是每次獲取到不同的對象。

好了,現在生命周期明了了,下面看一下注冊的方式。
我們通過這種方式進行注冊:

services.AddSingleton<IMySingletonService, MySingletonService>();

那么是否還有其他方式?

還可以這樣:

services.AddSingleton<IMySingletonService>(new MySingletonService());
services.AddSingleton<IMySingletonService>(ServiceProvider =>
{
	return new MySingletonService();
});

為什么需要這樣呢?道理也是很簡單的,因為我們可能需要一些自己定制的動態參數啊。
比如說:

services.AddSingleton<IMySingletonService>(ServiceProvider =>
{
	return new MySingletonService();
});

這種工廠模式,完全可以通過其他的注冊服務(ServiceProvider)的參數來實例化我們的MySingletonService。

還有值得注意的是,因為我們可以多次注入。那么我們按道理可以獲取到注冊的全部。可以通過這種方式。

比如說:

services.AddSingleton<IMySingletonService, MySingletonService>();
services.AddSingleton<IMySingletonService>(new MySingletonService());
services.AddSingleton<IMySingletonService>(ServiceProvider =>
{
	return new MySingletonService();
});

通過AddSingleton調用了3次。

[HttpGet]
public int GetService([FromServices] IEnumerable<IMySingletonService> mySingleton1)
{
	foreach (var item in mySingleton1)
	{
		Console.WriteLine(item.GetHashCode());
	}

	return 1;
}

然后獲取一下。

發現可以一對多了,分別獲取到3個不同注冊的。

那么如果我們的邏輯比較復雜,可能會多次調用到怎么破?

services.AddSingleton<IMySingletonService, MySingletonService>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMySingletonService, MySingletonService>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMySingletonService, MySingletonService>());

可以通過這種方式調用,如果注冊信息相同就不注冊,如果不同就注冊。

這樣就只有一個。

如果你只想注冊一個的話可以這樣:

services.AddSingleton<IMySingletonService, MySingletonService>();
services.TryAddSingleton<IMySingletonService, MySingletonService>();
services.TryAddSingleton<IMySingletonService, MySingletonService>();

這個就是如果有注冊信息,就不再注冊了。

還有其他的如替換注冊信息,移除注冊信息等,可以參考官網。

然后可以關注一下這個using Microsoft.Extensions.DependencyInjection.Extensions;,這里面是一些擴展的,如有自己需要的一般都在這里面了。

另一個值得注意的地方就是泛型模板的注冊。

public interface IGeneticService<T>
{
	public T getData();
}

public class GeneticService<T>: IGeneticService<T>
{
	public T Data { get; private set; }

	public GeneticService(T data)
	{
		this.Data = data;
	}

	public T getData()
	{
		return Data;
	}
}

那么通過這種注冊:

services.AddSingleton(typeof(IGeneticService<>),typeof(GeneticService<>));

然后實現一個接口:

[HttpGet]
public int GetService([FromServices] IMySingletonService mySingletonService, [FromServices] IGeneticService<IMySingletonService> geneticService)
{
	Console.WriteLine(geneticService.getData().GetHashCode());
	Console.WriteLine(mySingletonService.GetHashCode());
	return 1;
}

這里埋了一個點:

services.AddSingleton<IMySingletonService, MySingletonService>();
services.AddSingleton<IMySingletonService>(new MySingletonService());
services.AddSingleton<IMySingletonService>(ServiceProvider =>
{
	return new MySingletonService();
});

那就是上面這三種注冊方式的對象釋放行為是不一樣的,如果不了解的話,那么可能你的服務跑着跑着內存就跑高了。下一節將會解釋到。

上述只是個人整理,如有錯誤,望請指出,謝謝,一天一更。上述只是應用,如代碼原理,請查看<<重新整理.net core 1400篇>>。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM