基于DDD的.NET开发框架 - ABP启动配置
ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。
ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WEB应用程序框架和项目模板。
ABP的官方网站:http://www.aspnetboilerplate.com
ABP官方文档:http://www.aspnetboilerplate.com/Pages/Documents
Github上的开源项目:https://github.com/aspnetboilerplate
一、如何把带zero模块的abp运行起来
今天我们在官网生成模版的页面:http://www.aspnetboilerplate.com/Templates 生成一个带zero模块的abp解决方案。
生成完了后打开,解决目录结构如下:
执行Code First Migrations 两个命令:
Add-Migration “AbpZero_Installed”
Update-Databse
但总是执行失败如下图:
我们注意到 Data Scource 竟然是 .\\SQLEXPRESS 而不是我们想要的 localDB 。 如果我们安装了 SQL Express,那么 database 将会安装在 local SQL Express instance,否则 Code First 才将尝试使用 localDB。
最好的解决办法就是默认信任连接字符串换成标准安全连接,问题解决
信任连接
<add name="FirstCodeFirstApp" connectionString="Server=.;Database=CodeFirstApp;Integrated Security=SSPI" providerName="System.Data.SqlClient"/>
标准安全连接
<connectionStrings> <add name="Default" connectionString="Data Source =.;Initial Catalog = FirstAbpZero;User Id = sa;Password = 123;" providerName="System.Data.SqlClient" /> </connectionStrings>
运行起来用默认的用户名:admin 密码:123qwe 登录,也可以自己注册一个,但是默认租户要填写default;登录页面
注册页面
admin登录后添加用户页面
虽然运行起来了好像和今天要说的配置关系不大,下面进入正题。
二、ABP启动配置
配置ABP
public class SimpleTaskSystemModule : AbpModule { public override void PreInitialize() { //Add languages for your application Configuration.Localization.Languages.Add(new LanguageInfo("en", "English", "famfamfam-flag-england", true)); Configuration.Localization.Languages.Add(new LanguageInfo("tr", "Türkçe", "famfamfam-flag-tr")); //Add a localization source Configuration.Localization.Sources.Add( new XmlLocalizationSource( "SimpleTaskSystem", HttpContext.Current.Server.MapPath("~/Localization/SimpleTaskSystem") ) ); //Configure navigation/menu Configuration.Navigation.Providers.Add<SimpleTaskSystemNavigationProvider>(); } public override void Initialize() { IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); } }
abp框架一开始就被设计成模块化的,不同的模块可以通过abp框架来进行配置。举个例子吧,不同的模块都可以添加导航,通过导航添加菜单项到自己定义的主菜单,具体的细节大家可以参照:
本地化:http://www.aspnetboilerplate.com/Pages/Documents/Localization
导航:http://www.aspnetboilerplate.com/Pages/Documents/Navigation
重写内置服务
可以通过Configuration.ReplaceService重写服务,比如可以与您的自定义实现替代iabpsession服务如下图所示:
Configuration.ReplaceService<IAbpSession, MySession>(DependencyLifeStyle.Transient);
replaceservice方法具有过载通过一个自定义的方式进行替换操作。
同样的服务可以被替换多次(特别是在不同的模块)。最后更换将是有效的(你知道,模块分发方法是通过依赖顺序执行)。
配置模块
和.net框架原生的启动配置相比较,abp有哪些不一样呢?abp框架的模块可以通过IAbpModuleConfigurations接口进行个性化的扩展,这样的话,模块配置更加简单、方便。
示例代码如下:
... using Abp.Web.Configuration; ... public override void PreInitialize() { Configuration.Modules.AbpWeb().SendAllExceptionsToClients = true; } ...
在上面这个例子中,我们通过配置AbpWeb模块,发送异常到客户端。当然了,不是每一个模块都需要这种配置,通常情况下我们需要,是当一个模块需要在多个不同的应用中重复使用,我们才进行这样的配置。
为一个模块创建配置
如下代码,假如我们有一个命名为MyModule的模块,并且这各模块有一些自己的配置。那么我们首先要创建一些类,这些类定义为属性(译者注:属性有自动的get和set访问器。),代表了不同的配置。
public class MyModuleConfig { public bool SampleConfig1 { get; set; } public string SampleConfig2 { get; set; } }
接下来,我们通过依赖注入,注册这个类。
IocManager.Register<MyModuleConfig>();
最后,我们通过创建一个扩展的方法IModuleConfigurations来得到配置的引用。如下代码:
public static class MyModuleConfigurationExtensions { public static MyModuleConfig MyModule(this IModuleConfigurations moduleConfigurations) { return moduleConfigurations.AbpConfiguration .GetOrCreate("MyModuleConfig", () => moduleConfigurations.AbpConfiguration.IocManager.Resolve<MyModuleConfig>() ); } }
现在,在其他模块中也可以配置我们自定义的这个MyModule模块了。
Configuration.Modules.MyModule().SampleConfig1 = false; Configuration.Modules.MyModule().SampleConfig2 = "test";
在某种意义上,MyModule需要这些配置,你能注射MyModuleConfig并且可以使用这些值。
public class MyService : ITransientDependency { private readonly MyModuleConfig _configuration; public MyService(MyModuleConfig configuration) { _configuration = configuration; } public void DoIt() { if (_configuration.SampleConfig2 == "test") { //... } } }
这意味着,在abp框架的系统中,所有的模块都可以集中配置。
三、ABP核心模块配置分析
ABP的配置设计的非常巧妙,通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配置中心化。配置中心化是一个支持模块开发的框架必备功能。
ABP中核心功能模块中的一些功能的运行时的行为是依赖于一些外部配置的。所以ABP底层框架通过IAbpStartupConfiguration供外部模块自定义Congfiguration。
ABP初始化阶段就要被实例化的接口都放到了Startup文件夹下
ABP核心功能组件的Configuration实例化过程如下图:
以后需要调用或修改某个组件的Configuration,只要引用其IAbpStartupConfiguration的实例Configuration即可访问各个组件的Configuration了。
四、自定义module的Configuration 实现分析
Abp底层框架知道自己核心功能模块,所以就很自然的给每个核心功能模块各自定义一个Configuration接口。但是Abp底层框架并不知道自定义的module有哪些,所以也就无法给每个自定义的module都创建一个Configuration接口。那么ABP底层框架是怎么管理每个自定义的module的Configuration的呢?
仔细看代码,IAbpStartupConfiguration继承了IDictionaryBasedConfig,DictionaryBasedConfig实现了IDictionaryBasedConfig接口,DictionaryBasedConfig下有个dictionary,这个就是最终保存自定义的module的Configuration的地方,因为Configuration的类型未知,所以dictionary的value是object类型。
下面以AbpWebModule为例讲解
1、AbpWebModule定义了自己的接口IAbpWebModuleConfiguration和AbpWebModuleConfiguration,以及AbpConfigurationExtension
2、在AbpWebModule的preInitialize方法中将接口IAbpWebModuleConfiguration和其实现AbpWebModuleConfiguration注册到容器中。
IocManager.Register<IAbpWebModuleConfiguration, AbpWebModuleConfiguration>();
3、AbpConfigurationExtension扩展了IModuleConfigurations接口
using Abp.Web.Configuration; namespace Abp.Configuration.Startup { /// <summary> /// Defines extension methods to <see cref="IModuleConfigurations"/> to allow to configure ABP Web module. /// </summary> public static class AbpWebConfigurationExtensions { /// <summary> /// Used to configure ABP Web module. /// </summary> public static IAbpWebModuleConfiguration AbpWeb(this IModuleConfigurations configurations) { return configurations.AbpConfiguration.GetOrCreate("Modules.Abp.Web", () => configurations.AbpConfiguration.IocManager.Resolve<IAbpWebModuleConfiguration>()); } } }
这个接口是在Abp底层框架中定义和实现的。其属性AbpConfiguration就是AbpStartupConfiguration实例。通过AbpStartupConfiguration的getorCreate方法(具体通过DictionaryBasedConfig来实现)就可以获取到AbpWebModule自定义的AbpWebModuleConfiguration的实例了。
namespace Abp.Configuration.Startup { internal class ModuleConfigurations : IModuleConfigurations { public IAbpStartupConfiguration AbpConfiguration { get; private set; } public ModuleConfigurations(IAbpStartupConfiguration abpConfiguration) { AbpConfiguration = abpConfiguration; } } }
具体访问方式如下,Configuration.Modules就是IModuleConfigurations的实例。通过其扩展方法AbpWeb访问AbpWebModuleConfiguration
Configuration.Modules.AbpWeb.SendAllExceptionsToClients=True;