依赖注入(Dependency Injection,简称DI)是为了实现各个类之间的依赖的控制反转(Inversion of Control,简称IoC )。
ASP.NET Core 中的Controller 和 Service 或者其他类都支持依赖注入。
依赖注入术语中,
Service 是一个为其他对象提供服务的类**。
Service 不是一个Web Service,与Web Service无关。
Service的使用方法一般是:
- 在Main函数中注册Register到容器中,可以使用ASP.NET Core 内置的容器或者第三方容器,比如Autofac。
- 在注册过的类的构造函数中即可将其他依赖类当作入参Resolve。
- 或者通过IServiceScopeFactory 来Resolve。
- 容器负责Dispose。
比如:
定义接口:
public interface IMyDependency
{
void WriteMessage(string message);
}
定义实现类:
public class MyDependency : IMyDependency
{
public void WriteMessage(string message)
{
Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
}
}
在Services容器中注册类:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IMyDependency, MyDependency>();
builder.Services.AddScoped<PageModel, IndexModel>();
var app = builder.Build();
app.Run();
使用构造函数Resolve依赖对象:
public class IndexModel : PageModel
{
private readonly IMyDependency myDependency;
public IndexModel(IMyDependency myDependency)
{
myDependency = myDependency;
}
}
这样不用在IndexModel 内部再new一个IMyDependency 类的对象,而是使用构造函数传入的对象。
而由容器来维护创建Service的对象的生命周期,这个过程叫做Resolve,对象共有3种生命周期:
- Transient,每次Resolve的时候都重新创建,即使在同一个Http Reuest中。
- Scoped,每次Http Reuest中,Resolve的时候重新创建,在该请求中不变。
- Singleton,第一次Resolve的时候创建,后续Resolve都使用相同的对象。Singleton 的Service必须线程安全,因为所有线程都要用到,并且要考虑内存的用量。
注意:不应该在Singleton Service中Resolve Scoped类型的对象,反过来可以,因为可能导致Scoped类型的对象无法dispose。
使用IServiceScopeFactory 实现Resolve依赖对象:
public class IndexModel : PageModel
{
private readonly IServiceScopeFactory serviceScopeFactory;
public IndexModel(IServiceScopeFactory serviceScopeFactory)
{
serviceScopeFactory = serviceScopeFactory;
}
public DoSomething()
{
using (var scope = serviceScopeFactory .CreateScope())
{
// resolve a database connection
var db = scope.ServiceProvider.GetService<IDatabaseConnection>();
// do something with it
}
}
}
注册一组Service,类似实现一个
builder.Host.UseSerilog();
需要写一个ServiceCollection的扩展类,然后在实现中注册相关的类:
namespace Microsoft.Extensions.DependencyInjection
{
public static class MyConfigServiceCollectionExtensions
{
public static IServiceCollection AddConfig(
this IServiceCollection services, IConfiguration config)
{
services.Configure<PositionOptions>(
config.GetSection(PositionOptions.Position));
services.Configure<ColorOptions>(
config.GetSection(ColorOptions.Color));
return services;
}
public static IServiceCollection AddMyDependencyGroup(
this IServiceCollection services)
{
services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyDependency2, MyDependency2>();
return services;
}
}
}
然后就可以这样注册了
builder.Services
.AddConfig(builder.Configuration)
.AddMyDependencyGroup();
如何设计项目中的依赖Service:
- 应当避免有状态的,静态的类。
- 应当避免地App中创建全局对象,而应该使用singleton services。
- 应当避免直接在service中初始化依赖的类,这样会增加耦合。
- 应当保证service功能单一,以便测试。
- 如果类中有大量的依赖注入,说明这个类的功能过于复杂,应当拆分,使其单一职责。
Service的Dispose:文章来源:https://www.toymoban.com/news/detail-672176.html
- Singleton类型的 Service 不应该添加Dispose方法,App结束进程时会自动dispose。
- Scoped和Transient类型的Service 应该添加Dispose方法,容器会自动调用。
其余建议文章来源地址https://www.toymoban.com/news/detail-672176.html
- 不支持 async/await 注入,因为C#不支持async构造函数。
- 不直接在容器中持久保存数据。
- 配置项应该使用options pattern。
- 不应该static访问service。
- 尽量让DI工厂的操作:同步,快速。
- 通在构造函数中注入的时候,不应该使用 service locator pattern。
- 配置service的时候不应该调用 BuildServiceProvider,而只应该在注册B service时需要resolve A service的时候才用。
- 开启Scope validation,避免scoped service中使用singletons service。
- container 直接resolve service,可能会导致内存泄露,比如:
static void TransientDisposablesWithoutDispose()
{
var services = new ServiceCollection();
services.AddTransient<ExampleDisposable>();
ServiceProvider serviceProvider = services.BuildServiceProvider();
for (int i = 0; i < 1000; ++ i)
{
_ = serviceProvider.GetRequiredService<ExampleDisposable>();
}
//1,000 个对象会被创建,serviceProvider 不dispose,这1000个对象就不dispose。
}
到了这里,关于ASP.NET Core 中的 Dependency injection的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!