asp.net 依赖注入
在本文中,我们将做一些练习来介绍ASP.NET中DI(依赖注入)的基础知识。
辅助功能级别
以下代码的结果是什么?
public interface IServiceA { }
class ServiceA : IServiceA
{
ServiceA()
{
Console.WriteLine( "New SA" );
}
}
public class Startup
{
public void ConfigureServices ( IServiceCollection services )
{
services.AddTransient<IServiceA, ServiceA>();
...
}
}
它将引发异常:
System.AggregateException: 'Some services are not able to be constructed'
A suitable constructor for type 'AspNetCore.Services.ServiceA' could not be located.
Ensure the type is concrete and services are registered for all parameters of a public constructor.
如ASP.NET Core文档中所述 ,构造函数注入需要公共构造函数。
为什么是公共构造函数? 因为构造函数的默认可访问性级别为private。 DI是由ASP.NET Core实现的,除public外,它无法访问其他级别。
为什么不公开课? 因为DI 框架已经通过方法调用获取类。 如果我们要使用名称空间访问类,则需要public。
生命周期
当以下应用运行且没有任何请求到来时,是否将初始化单例服务?
public interface IServiceA { }
public class ServiceA : IServiceA
{
public ServiceA ()
{
Console.WriteLine( "New SA" );
}
}
public class Startup
{
public void ConfigureServices ( IServiceCollection services )
{
services.AddSingleton<IServiceA, ServiceA>();
...
}
}
public class HelloController : ControllerBase
{
public WeatherForecastController ( IServiceA sa )
{
Console.WriteLine( $"Test Controller: {sa.GetHashCode()} " );
}
}
ServiceA将不会初始化。 DI 框架 m将检查构造函数(我想),但不会初始化实例。 仅在使用单例服务时才会对其进行初始化。
在这种情况下,服务取决于控制器,并且控制器将针对每个请求进行初始化。 因此,作为单例服务,将在第一个请求到来时初始化ServiceA。 当第一个请求之后出现其他请求时,单例服务将不再初始化:
// 1st request
New SA
Test Controller: 83452835
// 2nd request
Test Controller: 83452835
// 3rd request
Test Controller: 83452835
如果我们使用AddScoped而不是AddSingleton,则DI框架将为每个请求初始化一个新实例。
// 1st request
New SA
Test Controller: 72334852
// 2nd request
New SA
Test Controller: 83729442
// 3rd request
New SA
Test Controller: 19231424
初始化顺序
如果我们按AB的顺序注入依赖关系,并按BA的顺序获取依赖关系,那么哪个将首先初始化?
public interface IServiceA { }
public class ServiceA : IServiceA
{
public ServiceA ()
{
Console.WriteLine( "New SA" );
}
}
public interface IServiceB { }
public class ServiceB : IServiceB
{
public ServiceB ()
{
Console.WriteLine( "New SB" );
}
}
public class Startup
{
public void ConfigureServices ( IServiceCollection services )
{
services.AddSingleton<IServiceA, ServiceA>();
services.AddSingleton<IServiceB, ServiceB>();
...
}
}
public class HelloController : ControllerBase
{
public WeatherForecastController ( IServiceB sb, IServiceA sa )
{
Console.WriteLine( $"Test Controller: {sa.GetHashCode()} " );
}
}
结果:
New SB
New SA
Test Controller:83427434 22834295
尽管注入顺序是AB,但是因为我们先获取B,所以B将首先被初始化。
如果B依赖于A怎么办?
public class ServiceB : IServiceB
{
public ServiceB ( IServiceA sa )
{
Console.WriteLine( $"New SB with sa: {sa.GetHashCode()} " );
}
}
结果:
New SA
New SB with sa:46284926
Test Controller: 46284926 64753745
将首先初始化ServiceA,然后初始化ServiceB。 给定ServiceA是单例,将不会再次初始化。
如果我们在A之前注入B但B取决于A怎么办?
public void ConfigureServices ( IServiceCollection services )
{
services.AddScoped<IServiceB, ServiceB>();
services.AddScoped<IServiceA, ServiceA>();
}
结果:
New SA
New SB with sa:72362183
Test Controller: 72362183 91218567
注射顺序无关紧要。 DI容器将保留接口和Implement类之间的关系,并在初始化时为我们正确处理所有事情。
一接口多机具
如果有多个工具类并且都被注入,那么将初始化哪一个?
public interface IServiceA { }
public class ServiceA : IServiceA
{
public ServiceA ()
{
Console.WriteLine( "New SA" );
}
}
public class ServiceB : IServiceA
{
public ServiceB ()
{
Console.WriteLine( "New SB" );
}
}
public class Startup
{
public void ConfigureServices ( IServiceCollection services )
{
services.AddSingleton<IServiceA, ServiceA>();
services.AddSingleton<IServiceA, ServiceB>();
...
}
}
public class HelloController : ControllerBase
{
public WeatherForecastController ( IServiceA sa )
{
Console.WriteLine( $"Test Controller: {sa.GetHashCode()} " );
}
}
结果:
New SB
Test Controller:46399782
仅最后一个将被初始化。 如果要第一个,请使用TryAdd。 如果给定的服务接口已经存在注册,则TryAdd将不起作用。
多接口一机具
如果有多个接口,并且只有一个实现类注册为单例,它将被初始化多少次?
public interface IServiceA { }
public interface IServiceB { }
public class ServiceB : IServiceA , IServiceB
{
public ServiceB ()
{
Console.WriteLine( "New SB" );
}
}
public class Startup
{
public void ConfigureServices ( IServiceCollection services )
{
services.AddSingleton<IServiceA, ServiceB>();
services.AddSingleton<IServiceB, ServiceB>();
...
}
}
public class HelloController : ControllerBase
{
public WeatherForecastController ( IServiceA sa, IServiceB sb )
{
Console.WriteLine( $"Test Controller: {sa.GetHashCode()} {sb.GetHashCode()} " );
}
}
结果:
New SB
New SB
Test Controller:78346274
如您所见,“ AddSingleton”中的单例是指接口而不是工具。 ServicesB是两个接口的实现类,它们将分别初始化。
跟进
所有这些练习都是非常基本且通用的。 有些部分只是我个人的看法。 我是ASP.NET Core的初学者,如果您认为有任何问题,请更正。
如果您想了解有关ASP.NET Core中DI的更多信息,那么Microsoft.Extensions.DependencyInjection的源代码是不错的阅读方法。
参考资料
- Microsoft.Extensions.DependencyInjection源代码
- 依赖注入| ASP.NET核心文档
- 了解.NET Core中的依赖项注入
- ASP.NET核心依赖注入深入研究
- ASP.NET核心依赖项注入
- .Net核心依赖项注入
- 如何在ASP.Net Core中使用依赖项注入
翻译自: https://hackernoon.com/exercises-on-dependency-injection-in-aspnet-introduction-lb173u6k
asp.net 依赖注入