Abp vNext 依赖注入
介绍
ABP的依赖注入系统是基于Microsoft的依赖注入扩展库(Microsoft.Extensions.DependencyInjection nuget包)开发的。所以我们采用dotnet自带的注入方式也是支持的。
- 由于ABP是一个模块化框架,因此每个模块都定义它自己的服务并在它自己的单独模块类中通过依赖注入进行注册.例:
public class BlogModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
//在此处注入依赖项
// dotnet自带依赖注入方式也是支持的
context.services.AddTransient
context.services.AddScoped
context.services.AddSingleton
}
}
Autofac
Autofac 是.Net世界中最常用的依赖注入框架之一. 相比.Net Core标准的依赖注入库, 它提供了更多高级特性, 比如动态代理和属性注入.
集成
1.安装 Volo.Abp.Autofac nuget 包到你的项目 (对于一个多项目应用程序, 建议安装到可执行项目或者Web项目中.)
2.模块添加 AbpAutofacModule 依赖:
[DependsOn(typeof(AbpAutofacModule))]
public class MyModule : AbpModule
{
//...
}
}
3.配置 AbpApplicationCreationOptions 用 Autofac 替换默认的依赖注入服务. 根据应用程序类型, 情况有所不同
- ASP.NET Core 应用程序
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!
}
- 控制台应用程序
namespace AbpConsoleDemo
{
class Program
{
static void Main(string[] args)
{
using (var application = AbpApplicationFactory.Create<AppModule>(options =>
{
options.UseAutofac(); //Autofac integration
}))
{
//...
}
}
}
}
依照约定的注册
如果实现这些接口,则会自动将类注册到依赖注入:
- ITransientDependency 注册为transient生命周期.
- ISingletonDependency 注册为singleton生命周期.
- IScopedDependency 注册为scoped生命周期.
默认特定类型
一些特定类型会默认注册到依赖注入.例子:
- 模块类注册为singleton.
- MVC控制器(继承Controller或AbpController)被注册为transient.
- MVC页面模型(继承PageModel或AbpPageModel)被注册为transient.
- MVC视图组件(继承ViewComponent或AbpViewComponent)被注册为transient.
- 应用程序服务(实现IApplicationService接口或继承ApplicationService类)注册为transient.
- 存储库(实现IRepository接口)注册为transient.
- 域服务(实现IDomainService接口)注册为transient.
手动注册
在某些情况下,你可能需要向IServiceCollection手动注册服务,尤其是在需要使用自定义工厂方法或singleton实例时.在这种情况下,你可以像Microsoft文档描述的那样直接添加服务.
public class BlogModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.services.AddTransient<ITestMicrosoftManager, TestMicrosoftManager>();
}
}
如何使用
构造函数注入
- 构造方法注入是将依赖项注入类的首选方式
属性注入
- Microsoft依赖注入库不支持属性注入.但是,ABP可以与第三方DI提供商(例如Autofac)集成,以实现属性注入。
- 属性注入依赖项通常被视为可选依赖项.这意味着没有它们,服务也可以正常工作.Logger就是这样的依赖项,MyService可以继续工作而无需日志记录.
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
public MyService()
{
Logger = NullLogger<MyService>.Instance;
}
public void DoSomething()
{
//...使用 Logger 写日志...
}
}
IServiceProvider
直接从IServiceProvider解析服务.在这种情况下,你可以将IServiceProvider注入到你的类并使用
public class MyService : ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public MyService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void DoSomething()
{
var taxCalculator = _serviceProvider.GetService<ITaxCalculator>();
//...
}
}
服务替换
在某些情况下,需要替换某些接口的实现.
- ITestManager有一个默认实现DefaultManager,但是我现在想替换成TestReplaceManager,该如何操作呢?
原生dotnet方式替换
services.Replace(ServiceDescriptor.Transient<ITestManager, TestReplaceManager>());
Abp支持
- 加上Dependency特性标签
- 加上ExposeServices特性标签
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ITestManager))]
public class TestReplaceManager : ITestManager, ITransientDependency
{
public void Print()
{
Console.WriteLine("TestReplaceManager");
}
}
问题
- 有时候我们实现了ITransientDependency,ISingletonDependency,IScopedDependency但是再运行是还是提示依赖注入失败?
- 实现类的名称拼写错误
- 比如接口名称为ITestAppService,但是实现类为DefaultTessAppService,这个时候编译不会报错,但是运行报错,下面会基于源码分析。
public class DefaultTessAppService : ApplicationService, ITestAppService
{
// ....
}
- 我通过[Dependency(ReplaceServices = true)]替换服务没有生效?
- 请添加[ExposeServices(typeof(ITestManager))]显示暴露服务,下面会基于源码分析。
源码分析
- 进入到Startup.AddApplication源码
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<xxxManagementHttpApiHostModule>();
}
}
- 进入到await app.ConfigureServicesAsync()源码
public async static Task<IAbpApplicationWithExternalServiceProvider> CreateAsync(
[NotNull] Type startupModuleType,
[NotNull] IServiceCollection services,
Action<AbpApplicationCreationOptions>? optionsAction = null)
{
var app = new AbpApplicationWithExternalServiceProvider(startupModuleType, services, options =>
{
options.SkipConfigureServices = true;
optionsAction?.Invoke(options);
});
await app.ConfigureServicesAsync();
return app;
}
- 主要查看ConfigureServices下的Services.AddAssembly(assembly)方法。
public virtual async Task ConfigureServicesAsync()
{
// 省略...
var assemblies = new HashSet<Assembly>();
//ConfigureServices
foreach (var module in Modules)
{
if (module.Instance is AbpModule abpModule)
{
if (!abpModule.SkipAutoServiceRegistration)
{
var assembly = module.Type.Assembly;
if (!assemblies.Contains(assembly))
{
Services.AddAssembly(assembly);
assemblies.Add(assembly);
}
}
}
try
{
await module.Instance.ConfigureServicesAsync(context);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServicesAsync)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
// 省略...
}
4.进入下面AddAssembly下AddType的逻辑
public class DefaultConventionalRegistrar : ConventionalRegistrarBase
{
public override void AddType(IServiceCollection services, Type type)
{
if (IsConventionalRegistrationDisabled(type))
{
return;
}
// 查看是否有DependencyAttribute特性标签
var dependencyAttribute = GetDependencyAttributeOrNull(type);
// 判断是否有实现接口,注入对于的类型。
var lifeTime = GetLifeTimeOrNull(type, dependencyAttribute);
if (lifeTime == null)
{
return;
}
var exposedServiceTypes = GetExposedServiceTypes(type);
TriggerServiceExposing(services, type, exposedServiceTypes);
foreach (var exposedServiceType in exposedServiceTypes)
{
var serviceDescriptor = CreateServiceDescriptor(
type,
exposedServiceType,
exposedServiceTypes,
lifeTime.Value
);
if (dependencyAttribute?.ReplaceServices == true)
{
services.Replace(serviceDescriptor);
}
else if (dependencyAttribute?.TryRegister == true)
{
services.TryAdd(serviceDescriptor);
}
else
{
services.Add(serviceDescriptor);
}
}
}
}
// GetLifeTimeOrNull
protected virtual ServiceLifetime? GetLifeTimeOrNull(Type type, DependencyAttribute? dependencyAttribute)
{
return dependencyAttribute?.Lifetime ?? GetServiceLifetimeFromClassHierarchy(type) ?? GetDefaultLifeTimeOrNull(type);
}
// abp 三个生命周期
protected virtual ServiceLifetime? GetServiceLifetimeFromClassHierarchy(Type type)
{
if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type))
{
return ServiceLifetime.Transient;
}
if (typeof(ISingletonDependency).GetTypeInfo().IsAssignableFrom(type))
{
return ServiceLifetime.Singleton;
}
if (typeof(IScopedDependency).GetTypeInfo().IsAssignableFrom(type))
{
return ServiceLifetime.Scoped;
}
return null;
}
5.重点到了,看下为什么名称错误为什么导致注入失败。
- 通过接口的名称去获取实现。
- 也能解释有时候不显示指定ExposeServices可能替换失败的问题
public class ExposeServicesAttribute : Attribute, IExposedServiceTypesProvider
{
// 省略...
private static List<Type> GetDefaultServices(Type type)
{
var serviceTypes = new List<Type>();
foreach (var interfaceType in type.GetTypeInfo().GetInterfaces())
{
var interfaceName = interfaceType.Name;
if (interfaceType.IsGenericType)
{
interfaceName = interfaceType.Name.Left(interfaceType.Name.IndexOf('`'));
}
// 查询到实现类的名称是否是移除I
if (interfaceName.StartsWith("I"))
{
interfaceName = interfaceName.Right(interfaceName.Length - 1);
}
// 查询到实现类的名称是否以接口名结尾
if (type.Name.EndsWith(interfaceName))
{
serviceTypes.Add(interfaceType);
}
}
return serviceTypes;
}
}
Abp vNext Pro
- Abp Vnext Pro Github地址 的 Vue3 实现版本 开箱即用的中后台前端/设计解决方案.
- 文档地址
- 演示地址:用户名admin 密码1q2w3E*
- Abp Vnext Pro Suite Github地址 代码生成器。
- 演示地址:用户名admin 密码1q2w3E*
- 视频教程
如果觉得可以,不要吝啬你的小星星哦
Abp vNext 依赖注入的更多相关文章
- ABP的依赖注入
目录 说说ABP的依赖注入 代码追踪 说说ABP的依赖注入 上篇abp运行机制分析分析了ABP在启动时,都做了那些事:这篇我们来说说ABP的最核心的一部分:依赖注入(DependencyInjecti ...
- ABP vNext 自动注入,暗藏天坑如斯
导言 我们在使用ABP vNext框架时,都知道该框架为我们实现了自动依赖注入(实现自动注入需要在项目里面创建Module类,并且将Module类上的DependsOn到相应的启动Module类或调用 ...
- ABP框架 - 依赖注入
文档目录 本节内容: 什么是依赖注入 传统方式的问题 解决方案 构造器注入模式 属性注入模式 依赖注入框架 ABP 依赖注入基础 注册依赖 约定注入 辅助接口 自定义/直接 注册 使用IocManag ...
- ABP之依赖注入
写在开头 ABP开源项目最近有点小火,还开展了线下活动.本着学习DDD的心态与学习开源代码的的好奇,我也看了一遍ABP源码,在此将自己学习ABP的一些心得记录下来. 作为核心的IoC 作为一种解耦的方 ...
- (译)ABP之依赖注入
原文地址:https://aspnetboilerplate.com/Pages/Documents/Dependency-Injection 什么是依赖注入 传统方式的问题 解决方案 构造函数注入 ...
- Asp.Net Core 3.1 Api 集成Abp项目依赖注入
Abp 框架 地址https://aspnetboilerplate.com/ 我们下面来看如何在自己的项目中集成abp的功能 我们新建core 3.1 API项目和一个core类库 然后 两个项目都 ...
- 3. abp依赖注入的分析.md
abp依赖注入的原理剖析 请先移步参考 [Abp vNext 源码分析] - 3. 依赖注入与拦截器 本文此篇文章的补充和完善. abp的依赖注入最后是通过IConventionalRegister接 ...
- ABP vNext系列文章03---依赖注入
一.依赖注入的类型注册 ABP的依赖注入系统是基于Microsoft的依赖注入扩展库(Microsoft.Extensions.DependencyInjection nuget包)开发的.因此,它的 ...
- ABP(现代ASP.NET样板开发框架)系列之6、ABP依赖注入
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之6.ABP依赖注入 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...
- ABP源码分析六:依赖注入的实现
ABP的依赖注入的实现有一个本质两个途径:1.本质上是依赖于Castle这个老牌依赖注入的框架.2.一种实现途径是通过实现IConventionalDependencyRegistrar的实例定义注入 ...
随机推荐
- EndNote参考文献格式Output Styles界面介绍
本文对EndNote软件修改论文参考文献引用格式的界面与各选项参数加以详细介绍. 利用EndNote软件进行论文参考文献的插入可以说是非常方便:但其亦具有一个问题,就是对中文文献的支持不太友好 ...
- STL-queue(ACM)
重构函数(默认) queue<int> q; 基本操作 q.front(); // 队列最前面的元素q.back(); // 队列最后面的元素q.size(); // 返回队列长度q.em ...
- 2023-06-16:给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。 我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。 所谓「表现良好的时间
2023-06-16:给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数. 我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」. 所谓「表现良好的时间 ...
- 智能合约HardHat框架环境的搭建
1.首先创建一个npm项目 PS C:\Users\lcds\blockchainprojects> mkdir hardhatcontract PS C:\Users\lcds\blockch ...
- IDEA的安装准备
IDEA的安装 第一步 第二步 第三部 第四步
- 构建 JavaScript ChatGPT 插件
聊天插件系统是一种令人兴奋的新方式,可以扩展ChatGPT的功能,纳入您自己的业务数据,并为客户与您的业务互动增加另一个渠道.在这篇文章中,我将解释什么是聊天插件,它们能做什么,以及你如何用JavaS ...
- node使用jsonwebtoken生成token与验证是否过期
场景 我们可以使用 cookie,session,token 来做鉴权. 下面我们来看一下, 如何使用 token 来做鉴权 jwt.sign 的简单介绍 npm install jsonwebtok ...
- fidder中勾选check for certificate revocations导致手机无法连上fidder服务器
在测试过程中因为要获取一个小程序登录API,就用手机设置代理后抓取,在抓取过程中发现提示用户名密码错误(未使用代理正常登录) 遂逐步查找,在与别人核对tools-option发现自己https页签中勾 ...
- CentOS 7相关操作
防火墙操作 开启防火墙 sudo systemctl start firewalld.service 查看防火墙状态 sudo systemctl status firewalld.service 关 ...
- git 访问仓库错误
通过https访问git出现错误, failed: Error in the pull function 尝试将https改为http