重新整理.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();
}

结果为:

也就是说在注册服务的时候就会报错。

总结

后面介绍服务注册的一些细节。

posted @ 2020-05-20 17:10  敖毛毛  阅读(358)  评论(0编辑  收藏  举报