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. [linux]使用curl进行GET、POST和网页调试

    以前在win下开发的时候,习惯用的调试工具是谷歌自带控制台和postman,谷歌控制台比较方便,但不能对请求进行重发,post虽然很好用,但是感觉还是有点'大'.在linux下,其实完全没有那么麻烦, ...

  2. ASP.NET Core 的 `Core` 有几种写法?

    一.概述 本文将会根据情况持续更新. 作为一个 Framework,ASP.NET Core 提供了诸多的扩展点.使用内置的组件和默认的配置通常就能够满足部分需求,当需要扩展的时就需要先去找出这些扩展 ...

  3. tensorflow保存读取-【老鱼学tensorflow】

    当我们对模型进行了训练后,就需要把模型保存起来,便于在预测时直接用已经训练好的模型进行预测. 保存模型的权重和偏置值 假设我们已经训练好了模型,其中有关于weights和biases的值,例如: im ...

  4. AtCoder Grand Contest 031 (AGC031) D - A Sequence of Permutations 其他

    原文链接https://www.cnblogs.com/zhouzhendong/p/AGC031D.html 前言 比赛的时候看到这题之后在草稿纸上写下的第一个式子就是 $$f(p,q) = pq^ ...

  5. twig模板的进一步学习以及在symfony当中的使用

    首先,twig可以理解为用于输出html代码的,虽然用PHP等其他语言也可以输出,但是twig更为简洁高效,同时twig模板被编译成原生的php类缓存起来,所以才会这么快, 其实twig跟php类差不 ...

  6. Flask速成项目:Flask实现计算机资源的实时监控

    很多人都说使用Python开发WEB应用非常方便,那么对于WEB新手来说,到底有多方便呢?本文即将展示给你Python的魔法. 本文将通过一个实例:Flask实现计算机资源的实时监控,迅速带你入门Fl ...

  7. 20165220《网络对抗技术》week1 Exp0 Kali安装

    下载地址: 地址:https://www.kali.org/downloads/ 安装: 登录 配置网络: 共享文件夹设置: 安装软件: 输入apt-get install ibus ibus-pin ...

  8. Analysis of Web.xml in Hello1 project

    一.web.xml文件介绍 The web.xml file contains several elements that are required for a Facelets applicatio ...

  9. web基础要点记录

    最近公司项目做完了,不怎么忙,翻看了一些基础的资料,文章.就做了个简单的记录. 1.Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示, 可通过加入 CSS 属性  -we ...

  10. python多线程和多进程使用

    # 多线程 from concurrent.futures import ThreadPoolExecutor # 多进程 from concurrent.futures import Process ...