ASP.NETCore依赖注入提议

有关服务的生命周期

  • 尽可能将您的服务注册为瞬态服务。 因为设计瞬态服务很简单。 您通常不用关心多线程和内存泄漏,并且您知道该服务的寿命很短。

  • 请谨慎使用Scoped,因为如果您创建子服务作用域或从非Web应用程序使用这些服务,则可能会非常棘手。

  • 谨慎使用Singleton,因为您需要处理多线程和潜在的内存泄漏问题。

  • 在Singleton服务中不要依赖Transient或者Scoped服务,因为如果当一个Singleton服务注入Transient服务,这个Transient服务就会变成一个Singleton服务,并且如果Transient服务不是为支持这种情况而设计的,则可能导致问题。 在这种情况下,ASP.NET Core的默认DI容器已经抛出异常。

有关注册的服务使用

  • 在我们注册服务后,通常使用的是构造函数注入的方式来注入所需依赖项
1
2
3
4
5
6
7
8
9
10
11
12
13
public class ProductService
{
private readonly IProductRepository _productRepository;
//构造函数注入
public ProductService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public void Delete(int id)
{
_productRepository.Delete(id);
}
}

而避免服务定位器,因为该模式存在隐含的依赖关系,这意味着在创建服务实例时无法轻松查看依赖关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ProductService
{
private readonly IProductRepository _productRepository;
//服务定位器注入
public ProductService(IServiceProvider serviceProvider)
{
_productRepository = serviceProvider
.GetRequiredService<IProductRepository>();
}
public void Delete(int id)
{
_productRepository.Delete(id);
}
public void ShowMes()
{
Console.WriteLine("Test");
}
}

但是有些种情况不适合使用构造函数注入,如注入的服务的实例的构造函数执行过于慢,如上面IProductRepository的实例,假设其构造函数需要花费1秒,那就会导致我们就算只是想访问ProductService的方法ShowMes()时,都需要等待至少1秒,这很糟糕,我们当然可以把该方法单独出来,但这明显不是一个很好的做法。

这时候我们就需要在使用时再用服务定位器方式获得其实例,如

1
2
3
4
5
6
public void Delete(int id)
{
_productRepository = serviceProvider
.GetRequiredService<IProductRepository>();
_productRepository.Delete(id);
}

而在Asp.Net Core中,还有使用FromServices特性修饰的办法

1
2
3
4
5
6
7
8
public class ValuesController : Controller
{
[HttpGet]
public ActionResult<bool> Get([FromServices] IAccountService accountService, int accId)
{
return accountService.Validate(accID);
}
}
  • 将注入的依赖项分配给只读【readonly】字段/属性(防止在方法内意外地为其分配另外一个值)。
1
private readonly IProductRepository _productRepository;

获取当前注册的所有服务,并打印每个服务对应的声明类型、实现类型和生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/// <summary>
/// 获取当前注册的所有服务,并打印每个服务对应的声明类型、实现类型和生命周期
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public string GetDIShowList(IServiceCollection services)
{
StringBuilder sb = new StringBuilder();

sb.AppendLine($"Services Count: {services.Count}");
var provider = services.BuildServiceProvider();
foreach (var service in services)
{
var serviceTypeName = GetName(service.ServiceType);
var implementationType = service.ImplementationType
?? service.ImplementationInstance?.GetType()
?? service.ImplementationFactory?.Invoke(provider)?.GetType();
if (implementationType != null)
{
sb.AppendLine($"{service.Lifetime,-15} {serviceTypeName,-50}{GetName(implementationType)}");
}
}

return sb.ToString();

string GetName(Type type)
{
if (!type.IsGenericType)
{
return type.Name;
}
var name = type.Name.Split('`')[0];
var args = type.GetGenericArguments().Select(it => it.Name);
return $"{name}<{string.Join(",", args)}>";
}
}

//调用
public void ConfigureServices(IServiceCollection services)
{
var ShowServicesListOnConsole = "true"; //Configuration.GetSection("ShowServicesListOnConsole").Value;
if (!string.IsNullOrEmpty(ShowServicesListOnConsole) && ShowServicesListOnConsole.ToLower() == "true")
{
Console.WriteLine(GetDIShowList(services));
}
}

最终会显示类似

1
2
3
4
5
6
7
8
9
10
11
12
13
Services Count: 175
Singleton IHostingEnvironment HostingEnvironment
Singleton IHostEnvironment HostingEnvironment
Singleton HostBuilderContext HostBuilderContext
Singleton IConfiguration ConfigurationRoot
Singleton IApplicationLifetime ApplicationLifetime
Singleton IHostApplicationLifetime ApplicationLifetime
Singleton IHostLifetime ConsoleLifetime
Singleton IHost Host
Singleton IOptions<TOptions> UnnamedOptionsManager<TOptions>
Scoped IOptionsSnapshot<TOptions> OptionsManager<TOptions>
Singleton IOptionsMonitor<TOptions> OptionsMonitor<TOptions>
...