0.简要介绍

在 Abp 框架当中通过各种 Configuration 来实现模块的配置,Abp 本身提供的很多基础设施功能的一些在运行时的行为是通过很多不同的 Configuration 来开放给用户进行一些自定义配置的。

比如说缓存模块,我要配置缓存的过期时间,Abp 默认是 1 个小时,但是我也可以自己来定义,直接赋值或者从配置项来读取都是由具体使用者来控制的,所以 Abp 通过各种 Configuration 类来控制一些运行时参数。

这些 Abp 本身基础设施的配置类都是存放在 \Abp\src\Abp\Configuration\Startup\ 这个文件夹内部的,我们来看一下他们的依赖关系。

1.启动流程

从上图可以看到在 IAbpStartupConfiguration 内部拥有诸多引用(可能没有列举完成,可以在其定义看到),基本上 Abp 自己的基础设施配置都在这里面。

那么 IAbpStartupConfiguration 自己内部的这些属性是在哪儿初始化的呢,其实就是在之前讲过的 AbpBootstrapperInitialize() 内部初始化的。再看下代码:

public virtual void Initialize()
{
try
{
// 其他代码
IocManager.IocContainer.Install(new AbpCoreInstaller());
IocManager.Resolve<AbpStartupConfiguration>().Initialize();
// 其他代码
}
catch (Exception ex)
{
_logger.Fatal(ex.ToString(), ex);
throw;
}
}

AbpCoreInstaller 类内部之前也说过,在这里面统一注入了这些 Configuration 的单例,同时解析出 AbpStartupConfiguration ,调用其 Initialzie() 方法来对自己的那些 xxxConfiguration 接口赋值,代码如下:

public void Initialize()
{
Localization = IocManager.Resolve<ILocalizationConfiguration>();
Modules = IocManager.Resolve<IModuleConfigurations>();
Features = IocManager.Resolve<IFeatureConfiguration>();
Navigation = IocManager.Resolve<INavigationConfiguration>();
Authorization = IocManager.Resolve<IAuthorizationConfiguration>();
Validation = IocManager.Resolve<IValidationConfiguration>();
Settings = IocManager.Resolve<ISettingsConfiguration>();
UnitOfWork = IocManager.Resolve<IUnitOfWorkDefaultOptions>();
EventBus = IocManager.Resolve<IEventBusConfiguration>();
MultiTenancy = IocManager.Resolve<IMultiTenancyConfig>();
Auditing = IocManager.Resolve<IAuditingConfiguration>();
Caching = IocManager.Resolve<ICachingConfiguration>();
BackgroundJobs = IocManager.Resolve<IBackgroundJobConfiguration>();
Notifications = IocManager.Resolve<INotificationConfiguration>();
EmbeddedResources = IocManager.Resolve<IEmbeddedResourcesConfiguration>();
EntityHistory = IocManager.Resolve<IEntityHistoryConfiguration>(); CustomConfigProviders = new List<ICustomConfigProvider>();
ServiceReplaceActions = new Dictionary<Type, Action>();
}

所以,在模块定义的基类 AbpModule 当中,早就注入了 IAbpStartupConfiguration 接口,让你很方便的就可以在模块的预加载的时候配置各种基础设施的参数。举个栗子:

public override void PreInitialize()
{
Configuration.Caching.ConfigureAll(z=>z.DefaultSlidingExpireTime = TimeSpan.FromHours(1));
}

可以看到这里我们的 Configuration 属性其实就是 IAbpStartupConfiguration 接口。

2.代码分析

2.1自定义模块配置

我们可以看到 IAbpStartupConfiguration 除了自己拥有大量基础设施的配置类,同时他还继承一个基类叫做 DictionaryBasedConfig ,那么 Abp 框架为什么要这么写呢?

其实这个基类的作用就是存放用户自定义的 Configuration 类型的,细心观察的话会发现在 AbpStartupConfiguration 的内部有一个 Get 方法,该方法就是用来获取存储的配置类型。

public T Get<T>()
{
// 调用基类的 GetOrCreate 方法,不存在的话直接从 IocContainer 中解析
return GetOrCreate(typeof(T).FullName, () => IocManager.Resolve<T>());
}

DictionaryBasedConfig 中维护了一个字典 CustomSettings ,其 Key/Value 类型为 string/object ,因为在 Abp 框架当中是不知道你自定义模块配置类的类型的,所以存了一个 object 对象。

然后就有以下用法,首先在模块 PreInitialize() 方法当中注入你需要注入的配置类:

public override void PreInitialize()
{
// 注入配置类
IocManager.Register<IAbpAspNetCoreConfiguration, AbpAspNetCoreConfiguration>(); // 替换服务,后面讲解
Configuration.ReplaceService<IPrincipalAccessor, AspNetCorePrincipalAccessor>(DependencyLifeStyle.Transient);
Configuration.ReplaceService<IAbpAntiForgeryManager, AbpAspNetCoreAntiForgeryManager>(DependencyLifeStyle.Transient);
Configuration.ReplaceService<IClientInfoProvider, HttpContextClientInfoProvider>(DependencyLifeStyle.Transient);
}

然后针对 IModuleConfigurations 写一个扩展方法,因为在 IModuleConfigurations 内部就有一个 IAbpAspNetCoreConfiguration 的实例,IModuleConfigurations 的注释就说该接口是用于配置模块的,模块可以通过编写扩展方法来添加自己的 Configuration 类:

public static class AbpAspNetCoreConfigurationExtensions
{
/// <summary>
/// Used to configure ABP ASP.NET Core module.
/// </summary>
public static IAbpAspNetCoreConfiguration AbpAspNetCore(this IModuleConfigurations configurations)
{
// 两种写法都差不多
return configurations.AbpConfiguration.GetOrCreate("AbpModule", () => IocManager.Resolve<IAbpAspNetCoreConfiguration>());
return configurations.AbpConfiguration.Get<IAbpAspNetCoreConfiguration>();
}
}

2.2 服务实现替换

在 Abp 当中允许我们替换一些他本身的一些实现,只要你是在模块进行预加载的时候替换的话,都是可以的。而 Abp 他本身在 IAbpStartupConfiguration 当中提供了一个方法叫做 ReplaceService() 方法专门来让你替换服务。

我们来看一下他的定义:

void ReplaceService(Type type, Action replaceAction);

emmmm,传入一个 TypeAction,咋跟我看到的不一样呢,Ctrl + N 搜索了一下,发现在模块里面使用的 ReplaceService() 方法是存放在 AbpStartupConfigurationExtensions 里面编写的一个静态方法,其定义如下:

public static void ReplaceService<TType, TImpl>(this IAbpStartupConfiguration configuration, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
where TType : class
where TImpl : class, TType
{
configuration.ReplaceService(typeof(TType), () =>
{
configuration.IocManager.Register<TType, TImpl>(lifeStyle);
});
}

我来看看,传入一个 Type 和 一个 ActionType 用来调用 IAbpStartupConfiguration 的同名方法,Action 则是用来注册组件的。

原来如此,我们再来到 IAbpStartupConfiguration.ReplaceService(Type type, Action replaceAction) 的具体实现:

public Dictionary<Type, Action> ServiceReplaceActions { get; private set; }

public void ReplaceService(Type type, Action replaceAction)
{
ServiceReplaceActions[type] = replaceAction;
}

唔,就是一个字典嘛,我们来看看在什么地方用到过它。

public override void Initialize()
{
foreach (var replaceAction in ((AbpStartupConfiguration)Configuration).ServiceReplaceActions.Values)
{
replaceAction();
} // 其他代码
}

最后我们看到在 AbpKernelModuleInitialize() 方法里面就会遍历这个字典,来调用之前存入的 Action

因为 Abp 所有组件的注册都是在模块 Initialize() 内部来进行注册的,而这串代码刚好又放在 AbpKernelModule 的初始化方法的第一行就开始执行,所以确保你替换的组件能够在 Abp 内部组件注册前执行。

所以当你要替换 Abp 内置组件服务的时候一定要记住在模块的 PreInitialize() 里面执行哦~

3. 扩展:Abp 支持多数据库

如果你的 Abp 项目有多个数据库上下文实体的时候怎么办呢?

在 Abp 官方 Demo 当中就有说明,你可以通过替换默认的 IConnectionStringResolver 来实现不同数据库的解析哦~,我们继承 DefaultConnectionStringResolver 实现一个 MulitDbContextConnectionStringResolver

public class MulitDbContextConnectionStringResolver : DefaultConnectionStringResolver
{
public HKERPConnectionStringResolver(IAbpStartupConfiguration configuration)
: base(configuration)
{
} public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
{
if (args["DbContextConcreteType"] as Type == typeof(ADbContext))
{
var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
// 返回 ADbContext 的 ConnectionString
return configuration.GetConnectionString(AllConsts.ADbConnectionStringName);
} if (args["DbContextConcreteType"] as Type == typeof(BDbContext))
{
var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
// 返回 BDbContext 的 ConnectionString
return configuration.GetConnectionString(HKERPCRMConsts.BDbConnectionStringName);
} // 都不是则使用默认的数据库连接字符串 return base.GetNameOrConnectionString(args);
} }

然后在我们的 EFCore 模块的预加载方法当中加入以下代码:

Configuration.ReplaceService(typeof(IConnectionStringResolver), () =>
{
IocManager.IocContainer.Register(
Component.For<IConnectionStringResolver, IDbPerTenantConnectionStringResolver>()
.ImplementedBy<MulitDbContextConnectionStringResolver>()
.LifestyleTransient()
);
});

当然你也不要忘记在后面通过 AddDbContext() 方法来把你的数据库上下文添加到 Abp 里面去哦。

Configuration.Modules.AbpEfCore().AddDbContext<ADbContext>(options=>{ /*配置代码*/});
Configuration.Modules.AbpEfCore().AddDbContext<BDbContext>(options=>{ /*配置代码*/});

3.点此跳转到总目录

[Abp 源码分析]四、模块配置的更多相关文章

  1. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  2. ABP源码分析四十四:ZERO的配置

    ABP Zero模块中需要配置的地方主要集中在三块:配置静态的role,配置外部认证源,以及配置本地化语言和资源. UserManagementConfig/IUserManagementConfig ...

  3. ABP源码分析四十五:ABP ZERO中的EntityFramework模块

    AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...

  4. ABP源码分析四十六:ABP ZERO中的Ldap模块

    通过AD作为用户认证的数据源.整个管理用户认证逻辑就在LdapAuthenticationSource类中实现. LdapSettingProvider:定义LDAP的setting和提供Defaut ...

  5. ABP源码分析四:Configuration

    核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...

  6. ABP源码分析四十三:ZERO的本地化

    ABP Zero模块扩展了ABP基础框架中的本地化功能,实现了通过数据库管理本地化的功能.其通过数据库保存本地化语言及其资源. ApplicationLanguage:代表本地化语言的实体类.一种语言 ...

  7. ABP源码分析四十:ZERO的Application和Tenant

    ABP的Zero模块以数据库为数据源实现了ABP框架中的tenant management (multi-tenancy), role management, user management, ses ...

  8. ABP源码分析四十二:ZERO的身份认证

    ABP Zero模块通过自定义实现Asp.Net Identity完成身份认证功能, 对Asp.Net Identity做了较大幅度的扩展.同时重写了ABP核心模块中的permission功能,以实现 ...

  9. ABP源码分析四十七:ABP中的异常处理

    ABP 中异常处理的思路是很清晰的.一共五种类型的异常类. AbpInitializationException用于封装ABP初始化过程中出现的异常,只要抛出AbpInitializationExce ...

  10. ABP源码分析四十一:ZERO的Audit,Setting,Background Job

    AuditLog: 继承自Entity<long>的实体类.封装AuditLog的信息. AuditingStore: 实现了IAuditingStore接口,实现了将AuditLog的信 ...

随机推荐

  1. UML绘图工具——PlantUML

    1 简介 PlantUML是一个开源项目,支持通过简单直观的语言来定义以下UML图. 时序图 用例图 类图 活动图 组件图 状态图 对象图 部署图 定时图 支持生成图片格式有: PNG SVG LaT ...

  2. 百度地图Web引用

    上海中心二楼 示例 http://api.map.baidu.com/geocoder?address=北京市海淀区上地信息路9号奎科科技大厦&output=html&src=weba ...

  3. 在ASP.NET Core 中怎样使用 EF 框架读取数据库数据

    添加测试数据 我们首先使用 SQLite Studio 添加三条数据 ID Name 1 李白 2 杜甫 3 白居易 使用 SQLite Studio 打开我们的 blogging.db 数据库,双击 ...

  4. <玩转Django2.0>读书笔记:URL规则和视图

    1. 带变量的URL #urls.py from django.urls import path from .view import * urlpatterns = [ path('',index_v ...

  5. [JZOJ3588]【中山市选2014】J语言(表达式解析+栈)

    Description J语言作为一门编程语言,诞生于20世纪90年代.............. 好学的小H今天又学到了一种新东西——J语言.显然,J语言的背景已经被小H忘得一干二净了,但是小H仍然 ...

  6. login shell 和 non-login shell 的区别

              login shell:去的bash时需要完整的登录流程.就是说通过输入账号和密码登录系统,此时取得的shell称为login shell non-login shell:取得sb ...

  7. javascript js原生ajax post请求 实例

    HTML代码: 注意: xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencod ...

  8. ionic3+angular4开发混合app 之自定义组件

    这里主要是记录ionic3+angular4开发混合app时自定义组件,我想自定义组件的方法和angular4应该类似,具体在纯angular4中自定义组件,暂时没有实践,个人觉得差别不大,之后实践了 ...

  9. git 配置 .ssh key

    1.安装git软件: 2.打开本地git bash,使用如下命令生成ssh公钥和私钥对: ssh-keygen -t rsa -C 'xxx@xxx.com'    然后一路回车(-C 参数是你的邮箱 ...

  10. vue font-icon 图标

    1.vue 游览器左上角小图标 把.ico文件放在根目录下的static文件夹下,然后link标签引入 <link rel="shortcut icon" href=&quo ...