概念
ABP.vNext 是一个 ASP.NET Core的开源WEB应用程序框架,关于它的相关介绍可以查看官网(ABP Framework - Open Source Web Application Framework),本系列不过多赘述。
注入方式
ABP.vNext 的 Dependency Injection 是基于Microsoft dependency injection extension 库 (Microsoft.Extensions.DependencyInjection)来实现的。由于ABP的核心是模块化设计,每一个模块都定义了自己的服务注册类,所以你需要在你定义的模块中,重写 AbpModule 中的 ConfigureServices 方法。
ABP.vNext 采用约定配置的方式来实现依赖注入。在默认情况下,某些特定类型已注册到依赖注入容器中了,包括:
- Module 类默认将默认以 singleton的生命周期进行注入.
- MVC controllers (继承自Controller or AbpController) 默认将默认以 transient的生命周期进行注入.
- MVC page models (继承自PageModel or AbpPageModel) 默认将默认以 transient的生命周期进行注入.
- MVC view components (继承自ViewComponent or AbpViewComponent) 默认将默认以 transient的生命周期进行注入.
- Application services (继承自ApplicationService 类或其子类) 默认将默认以 transient的生命周期进行注入.
- Repositories (实现了BasicRepositoryBase 类或其子类) 默认将默认以 transient的生命周期进行注入.
- Domain services(实现 IDomainService 接口或继承自 DomainService 类) 将默认以 transient的生命周期进行注入.
除了以上默认情况下的特定类型会自动注册到DI容器中,你还可以通过实现ITransientDependency,ISingletonDependency,IScopedDependency接口来实现自动注入,如以下代码将TaxCalculator注册成为生命周期为transient的服务。
public class TaxCalculator : ITransientDependency
{
}
ABP还提供了 Attribute的方式来实现依赖注入,你可以通过添加 DependencyAttribute 特性来实现,该特性有三个属性
Lifetime
: 设置声明周期Singleton
,Transient
orScoped
.TryRegister
: 设置为true
时表示如果该服务之前未被注册过则注册此服务,如果之前被注册了,这里就不再注册了.ReplaceServices
: 设置为true
将替换之前该服务的注册记录.
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
public class TaxCalculator
{
}
需要注意的是,如果使用 DenpendencyAttribute 设置了 Lifetime,其优先级高于其他注入方式。
当一个服务同时实现了不同的接口时,可以使用 ExposeServicesAttribute
特性来暴露该服务注入时使用的是哪个接口。如果你不指定公开哪个接口,ABP 将采用命名约定的方式进行公开,下面的代码中,ICalculator, ITaxCalculator 就属于默认的命名约定。
[ExposeServices(typeof(ITaxCalculator))]
public class TaxCalculator: ICalculator, ITaxCalculator, ICanCalculate, ITransientDependency
{
}
除了上述的注入方式,ABP.vNext 还保留了手动注册的方式。
public class BlogModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
//Register an instance as singleton
context.Services.AddSingleton<TaxCalculator>(new TaxCalculator(taxRatio: 0.18));
//Register a factory method that resolves from IServiceProvider
context.Services.AddScoped<ITaxCalculator>(
sp => sp.GetRequiredService<TaxCalculator>()
);
}
}
如果需要替换已经存在的服务时,你除了使用 DependencyAttribute 指定 ReplaceServices
为 true
之外,还可以使用 Microsoft Dependency Injection库中的 IServiceCollection.Replace方法来实现。
public class MyModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
//Replacing the IConnectionStringResolver service
context.Services.Replace(ServiceDescriptor.Transient<IConnectionStringResolver, MyConnectionStringResolver>());
}
}
使用方式
构造函数注入
public class TaxAppService : ApplicationService
{
private readonly ITaxCalculator _taxCalculator;
public TaxAppService(ITaxCalculator taxCalculator)
{
_taxCalculator = taxCalculator;
}
public void DoSomething()
{
//...use _taxCalculator...
}
}
属性注入,Asp.NET Core自带的依赖注入并不支持属性注入,ABP.vNext中使用 Autofac来实现。
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
public MyService()
{
Logger = NullLogger<MyService>.Instance;
}
public void DoSomething()
{
//...use Logger to write logs...
}
}
通过IServiceProvider获取
public class MyService : ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public MyService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void DoSomething()
{
var taxCalculator = _serviceProvider.GetService<ITaxCalculator>();
//...
}
}
当一个接口被多个实现类实现时,使用时将会获取到最后一个注册的实现类。除非使用 IEnumerable<Interface>
去获取所有的实现类。
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient<IExternalLogger, ElasticsearchExternalLogger>();
context.Services.AddTransient<IExternalLogger, AzureExternalLogger>();
}
public class MyService : ITransientDependency
{
private readonly IEnumerable<IExternalLogger> _externalLoggers;
public MyService(IEnumerable<IExternalLogger> externalLoggers)
{
_externalLoggers = externalLoggers;
}
public async Task DemoAsync()
{
foreach (var externalLogger in _externalLoggers)
{
await externalLogger.LogAsync("Example log message...");
}
}
}
或许某些时候,你需要监听某些服务被注册时的行为,这时候可以使用 IServiceCollection.OnRegistred Event 来实现
public class AppModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(ctx =>
{
if (ctx.ImplementationType.IsDefined(typeof(MyLogAttribute), true))
{
ctx.Interceptors.TryAdd<MyLogInterceptor>();
}
});
}
}
集成Autofac
Asp.NET Core自带的依赖注入容器比较简单,Autofac 是 .Net 最常用的依赖注入框架之一。与 .Net Core 标准 DI 库相比,它提供了一些高级功能,例如动态代理和属性注入。
为你的项目添加 Volo.Abp.Autofac
nuget包,并在你的Module中添加
using Volo.Abp.Modularity;
using Volo.Abp.Autofac;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpAutofacModule))]
public class MyModule : AbpModule
{
//...
}
}
最后配置默认DI容器为Autofac
public class Program
{
public static int Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseAutofac(); //Integrate Autofac!
}