重新整理.net core 計1400篇[八] (.net core 中的依賴注入的深入理解)

生命週期策略

在實例Transient 和 Scoped 中,所以實現Idisposable 接口的服務實例會被當前IServiceProvider 對象保存起來,當IService 對象的Dispose 方法被調用的時候,這些服務實例的Dispose 方法被隨着調用。

在singleton由於服務實例保存在根容器的IserviceProvide 對象上,所以只有當後者的Dispose方法被調用的時候,這些服務實例的Dispose方法纔會隨之被調用。

在.net core 應用中,他具有一個與當前應用綁定代表的全局根容器的IServiceProvider對象。對於處理的每一次請求,ASP.Net Core框架會利用這個根容器來創建基於當前請求的服務範圍,並利用後者提供的IServiceProvider對象來提供請求處理所需的服務實例。

請求處理完成之後,創建的服務範圍被終結,那麼會調用IDisposable讓其他服務實例得到釋放。

服務註冊的驗證

爲什麼服務註冊需要驗證呢?

對於singleton和scoped 兩種不同生命週期是通過將提供的服務實例分別存放到作爲根容器的IServiceProvider對象和當前的IServiceProvider 對象來實現的。

這意味着作爲根容器提供的Scoped也是單例的。

在asp.net core應用中,將某個服務註冊的生命週期設置爲Scoped的真正意圖是希望依賴注入容器根據接收的每個請求來創建和釋放服務實例,但是一旦一個singleton引用了一個scoped,那麼scoped將會變成一個singleton。

可能這樣說有些模糊,代碼來示範一下:

static void Main()
{
	var root = new ServiceCollection()
		.AddSingleton<IFoo, Foo>()
		.AddScoped<IBar, Bar>()
		.BuildServiceProvider(true);
	var child = root.CreateScope().ServiceProvider;

	void ResolveService<T>(IServiceProvider provider)
	{
		var isRootContainer = root == provider ? "Yes" : "No";
		try
		{
			provider.GetService<T>();
			Console.WriteLine($"Status: Success; Service Type: { typeof(T).Name}; Root: { isRootContainer}");
		}
		catch (Exception ex)
		{
			Console.WriteLine($"Status: Fail; Service Type: { typeof(T).Name}; Root: { isRootContainer}");
			Console.WriteLine($"Error: {ex.Message}");
		}
	}

	ResolveService<IFoo>(root);
	ResolveService<IBar>(root);
	ResolveService<IFoo>(child);
	ResolveService<IBar>(child);
	Console.ReadKey();
}

public interface IFoo { }
public interface IBar { }
public class Foo : IFoo
{
	public IBar Bar { get; }
	public Foo(IBar bar) => Bar = bar;
}
public class Bar : IBar { }

是這樣的一個故事,在Foo中是一個單例,因爲Foo引用了Bar,那麼再Foo中Bar就是一個單例了。

上面有一個方法:.BuildServiceProvider(true);,設置爲true,那麼這個時候就會驗證Scoped模式是不是就真的就是scoped。

結果爲:

實際上,服務範圍的校驗中,是根據一個叫做ServiceProviderOptions來做設置的,剛剛我們設置的是ValidateScopes,也就是校驗範圍,但是這個校驗範圍是在我們創建服務實例的時候校驗的,那麼能不能在我們註冊的時候就校驗呢?

/// <summary>
/// Options for configuring various behaviors of the default <see cref="T:System.IServiceProvider" /> implementation.
/// </summary>
public class ServiceProviderOptions
{
	internal static readonly ServiceProviderOptions Default = new ServiceProviderOptions();

	/// <summary>
	/// <c>true</c> to perform check verifying that scoped services never gets resolved from root provider; otherwise <c>false</c>. Defaults to <c>false</c>.
	/// </summary>
	public bool ValidateScopes
	{
		[CompilerGenerated]
		get;
		[CompilerGenerated]
		set;
	}

	/// <summary>
	/// <c>true</c> to perform check verifying that all services can be created during <code>BuildServiceProvider</code> call; otherwise <c>false</c>. Defaults to <c>false</c>.
	/// NOTE: this check doesn't verify open generics services.
	/// </summary>
	public bool ValidateOnBuild
	{
		[CompilerGenerated]
		get;
		[CompilerGenerated]
		set;
	}

	internal ServiceProviderMode Mode
	{
		[CompilerGenerated]
		get;
		[CompilerGenerated]
		set;
	}
}

可以看到還有一個屬性,ValidateOnBuild,這個顯示當在構造的時候就會被驗證。

舉例子:

static void Main()
{

	BuildServiceProvider(false);
	BuildServiceProvider(true);

	void BuildServiceProvider(bool validateOnBuild)
	{
		try
		{
			var options = new ServiceProviderOptions
			{
				ValidateOnBuild = validateOnBuild
			};
			new ServiceCollection()
				.AddSingleton<IFoobar, Foobar>()
				.BuildServiceProvider(options);
			Console.WriteLine($"Status: Success; ValidateOnBuild: {validateOnBuild}");
		}
		catch (Exception ex)
		{
			Console.WriteLine($"Status: Fail; ValidateOnBuild: {validateOnBuild}");
			Console.WriteLine($"Error: {ex.Message}");
		}
	}
	Console.Read();
}

結果爲:

也就是說在註冊服務的時候就會報錯。

總結

後面介紹服務註冊的一些細節。

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