0.简要介绍

Abp 本身有两种设置,一种就是 上一篇文章 所介绍的模块配置 Configuration,该配置主要用于一些复杂的数据类型设置,不仅仅是字符串,也有可能是一些 C# 运行时的一些变量。另外一种则是本篇文章所讲的 Setting,Setting 主要用于配置一些简单的参数,比如 SMTP 地址,数据库连接字符串等一些基本的配置类型可以使用 Setting 来进行处理。

1.代码分析

1.1 启动流程

我们先来看一下设置是怎样被加入到 Abp 框架当中,并且是如何来使用它的。

在 Abp 框架内部开发人员可以通过 ISettingsConfiguration 的 Providers 属性来添加自己实现的 SettingProvider ,而 ISettingsConfiguration 的初始化是在上一篇文章所写的 AbpBootstrapper.Initialize() 里面进行初始化的。

开发人员通过继承 SettingProvider 来提供这些设置信息,并且在模块的 PreInitialize() 方法当中通过 Configuration 来添加书写好的配置提供者。

在模块进行初始化之后(也就是在 PostInitiailze() 方法内部),所有开发人员定义的 SettingProvider 通过 ISettingDefinitionManagerInitialize() 方法存储到一个 Dictionary 里面。

public sealed class AbpKernelModule : AbpModule
{// 其他代码public override void PostInitialize(){// 其他代码IocManager.Resolve<SettingDefinitionManager>().Initialize();// 其他代码}
}

Initialize() 方法内部:

private readonly IDictionary<string, SettingDefinition> _settings;public void Initialize()
{var context = new SettingDefinitionProviderContext(this);foreach (var providerType in _settingsConfiguration.Providers){using (var provider = CreateProvider(providerType)){foreach (var settings in provider.Object.GetSettingDefinitions(context)){_settings[settings.Name] = settings;}}}
}

对外则是通过 ISettingManager 来进行管理的。

所有的设置项是通过 ServiceProvider 来提供的。

设置的持久化配置则是通过 ISettingStore 来实现的,开发者可以通过替换 ISettingStore 的实现达到持久化到数据库或者是其他位置。

1.2 典型用法

1.2.1 设置提供者定义

internal class EmailSettingProvider : SettingProvider
{public override IEnumerable<SettingDefinition> GetSettingDefinitions(SettingDefinitionProviderContext context){return new[]{new SettingDefinition(EmailSettingNames.Smtp.Host, "127.0.0.1", L("SmtpHost"), scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(EmailSettingNames.Smtp.Port, "25", L("SmtpPort"), scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(EmailSettingNames.Smtp.UserName, "", L("Username"), scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(EmailSettingNames.Smtp.Password, "", L("Password"), scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(EmailSettingNames.Smtp.Domain, "", L("DomainName"), scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(EmailSettingNames.Smtp.EnableSsl, "false", L("UseSSL"), scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(EmailSettingNames.Smtp.UseDefaultCredentials, "true", L("UseDefaultCredentials"), scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(EmailSettingNames.DefaultFromAddress, "", L("DefaultFromSenderEmailAddress"), scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(EmailSettingNames.DefaultFromDisplayName, "", L("DefaultFromSenderDisplayName"), scopes: SettingScopes.Application | SettingScopes.Tenant)};}private static LocalizableString L(string name){return new LocalizableString(name, AbpConsts.LocalizationSourceName);}
}

1.2.2 注入设置提供者

public sealed class AbpKernelModule : AbpModule
{public override void PreInitialize(){// 其他代码Configuration.Settings.Providers.Add<EmailSettingProvider>();// 其他代码}
}

注入之后,那么相应的模块如何得到已经注入的配置项呢?

我们拿一个最直观的例子来展示一下,这里我们来到 Abp 项目的 Email 模块,来看看它是如何使用的。

public class DefaultMailKitSmtpBuilder : IMailKitSmtpBuilder, ITransientDependency
{private readonly ISmtpEmailSenderConfiguration _smtpEmailSenderConfiguration;public DefaultMailKitSmtpBuilder(ISmtpEmailSenderConfiguration smtpEmailSenderConfiguration){_smtpEmailSenderConfiguration = smtpEmailSenderConfiguration;}public virtual SmtpClient Build(){var client = new SmtpClient();try{ConfigureClient(client);return client;}catch{client.Dispose();throw;}}protected virtual void ConfigureClient(SmtpClient client){client.Connect(_smtpEmailSenderConfiguration.Host,_smtpEmailSenderConfiguration.Port,_smtpEmailSenderConfiguration.EnableSsl);if (_smtpEmailSenderConfiguration.UseDefaultCredentials){return;}client.Authenticate(_smtpEmailSenderConfiguration.UserName,_smtpEmailSenderConfiguration.Password);}
}

可以看到以上代码通过 ISmtpEmailSenderConfiguration 来拿到 SMTP 对应的主机名与端口号,那这与我们的 ISettingManager 又有何关系呢?

其实我们转到 ISmtpEmailSenderConfiguration 的实现 SmtpEmailSenderConfiguration 就清楚了。

public class SmtpEmailSenderConfiguration : EmailSenderConfiguration, ISmtpEmailSenderConfiguration, ITransientDependency
{/// <summary>/// SMTP Host name/IP./// </summary>public virtual string Host{get { return GetNotEmptySettingValue(EmailSettingNames.Smtp.Host); }}/// <summary>/// SMTP Port./// </summary>public virtual int Port{get { return SettingManager.GetSettingValue<int>(EmailSettingNames.Smtp.Port); }}/// <summary>/// User name to login to SMTP server./// </summary>public virtual string UserName{get { return GetNotEmptySettingValue(EmailSettingNames.Smtp.UserName); }}/// <summary>/// Password to login to SMTP server./// </summary>public virtual string Password{get { return GetNotEmptySettingValue(EmailSettingNames.Smtp.Password); }}/// <summary>/// Domain name to login to SMTP server./// </summary>public virtual string Domain{get { return SettingManager.GetSettingValue(EmailSettingNames.Smtp.Domain); }}/// <summary>/// Is SSL enabled?/// </summary>public virtual bool EnableSsl{get { return SettingManager.GetSettingValue<bool>(EmailSettingNames.Smtp.EnableSsl); }}/// <summary>/// Use default credentials?/// </summary>public virtual bool UseDefaultCredentials{get { return SettingManager.GetSettingValue<bool>(EmailSettingNames.Smtp.UseDefaultCredentials); }}/// <summary>/// Creates a new <see cref="SmtpEmailSenderConfiguration"/>./// </summary>/// <param name="settingManager">Setting manager</param>public SmtpEmailSenderConfiguration(ISettingManager settingManager): base(settingManager){}
}

在这里我们可以看到这些配置项其实是通过一个名字叫做 GetNotEmptySettingValue() 的方法来得到的,该方法定义在 SmtpEmailSenderConfiguration 的基类 EmailSenderConfiguration 当中。

public abstract class EmailSenderConfiguration : IEmailSenderConfiguration
{// 其他代码,已经省略/// <summary>/// Creates a new <see cref="EmailSenderConfiguration"/>./// </summary>protected EmailSenderConfiguration(ISettingManager settingManager){SettingManager = settingManager;}/// <summary>/// Gets a setting value by checking. Throws <see cref="AbpException"/> if it's null or empty./// </summary>/// <param name="name">Name of the setting</param>/// <returns>Value of the setting</returns>protected string GetNotEmptySettingValue(string name){var value = SettingManager.GetSettingValue(name);if (value.IsNullOrEmpty()){throw new AbpException($"Setting value for '{name}' is null or empty!");}return value;}
}

总而言之,如果你想要获取已经添加好的设置项,直接注入 ISettingManager 通过其 GetSettingValue() 就可以拿到这些设置项。

1.3 具体代码分析

Abp 系统设置相关的最核心的部分就是 ISettingManagerISettingDefinitionManagerISettingStoreSettingProviderSettingDefinition 下面就这几个类进行一些细致的解析。

1.3.1 SettingDefinition

在 Abp 当中,一个设置项就是一个 SettingDefinition,每个 SettingDefinition 的 Name 与 Value 是必填的,其中 Scopes 字段对应一个 SettingScopes 枚举,该属性用于确定这个设置项的使用应用范围。

public class SettingDefinition
{/// <summary>/// Unique name of the setting./// </summary>public string Name { get; private set; }/// <summary>/// Display name of the setting./// This can be used to show setting to the user./// </summary>public ILocalizableString DisplayName { get; set; }/// <summary>/// A brief description for this setting./// </summary>public ILocalizableString Description { get; set; }/// <summary>/// Scopes of this setting./// Default value: <see cref="SettingScopes.Application"/>./// </summary>public SettingScopes Scopes { get; set; }/// <summary>/// Is this setting inherited from parent scopes./// Default: True./// </summary>public bool IsInherited { get; set; }/// <summary>/// Gets/sets group for this setting./// </summary>public SettingDefinitionGroup Group { get; set; }/// <summary>/// Default value of the setting./// </summary>public string DefaultValue { get; set; }/// <summary>/// Can clients see this setting and it's value./// It maybe dangerous for some settings to be visible to clients (such as email server password)./// Default: false./// </summary>[Obsolete("Use ClientVisibilityProvider instead.")]public bool IsVisibleToClients { get; set; }/// <summary>/// Client visibility definition for the setting./// </summary>public ISettingClientVisibilityProvider ClientVisibilityProvider { get; set; }/// <summary>/// Can be used to store a custom object related to this setting./// </summary>public object CustomData { get; set; }public SettingDefinition(string name,string defaultValue,ILocalizableString displayName = null,SettingDefinitionGroup group = null,ILocalizableString description = null,SettingScopes scopes = SettingScopes.Application,bool isVisibleToClients = false,bool isInherited = true,object customData = null,ISettingClientVisibilityProvider clientVisibilityProvider = null){if (string.IsNullOrEmpty(name)){throw new ArgumentNullException(nameof(name));}Name = name;DefaultValue = defaultValue;DisplayName = displayName;Group = @group;Description = description;Scopes = scopes;IsVisibleToClients = isVisibleToClients;IsInherited = isInherited;CustomData = customData;ClientVisibilityProvider = new HiddenSettingClientVisibilityProvider();if (isVisibleToClients){ClientVisibilityProvider = new VisibleSettingClientVisibilityProvider();}else if (clientVisibilityProvider != null){ClientVisibilityProvider = clientVisibilityProvider;}}
}

1.3.2 ISettingManager

首先我们看一下 ISettingManager 的默认实现 SettingManager

public class SettingManager : ISettingManager, ISingletonDependency
{public const string ApplicationSettingsCacheKey = "ApplicationSettings";/// <summary>/// Reference to the current Session./// </summary>public IAbpSession AbpSession { get; set; }/// <summary>/// Reference to the setting store./// </summary>public ISettingStore SettingStore { get; set; }private readonly ISettingDefinitionManager _settingDefinitionManager;private readonly ITypedCache<string, Dictionary<string, SettingInfo>> _applicationSettingCache;private readonly ITypedCache<int, Dictionary<string, SettingInfo>> _tenantSettingCache;private readonly ITypedCache<string, Dictionary<string, SettingInfo>> _userSettingCache;/// <inheritdoc/>public SettingManager(ISettingDefinitionManager settingDefinitionManager, ICacheManager cacheManager){_settingDefinitionManager = settingDefinitionManager;AbpSession = NullAbpSession.Instance;SettingStore = DefaultConfigSettingStore.Instance;_applicationSettingCache = cacheManager.GetApplicationSettingsCache();_tenantSettingCache = cacheManager.GetTenantSettingsCache();_userSettingCache = cacheManager.GetUserSettingsCache();}
}

可以看到在这里面,他注入了 ISetingStoreISettingDefinitionManager ,并且使用了三个 ITypedCache 来为这些设置进行一个缓存。

下面这个 GetSettingValueAsync() 方法则是获取一个指定名称的设置值。

public Task<string> GetSettingValueAsync(string name)
{return GetSettingValueInternalAsync(name, AbpSession.TenantId, AbpSession.UserId);
}private async Task<string> GetSettingValueInternalAsync(string name, int? tenantId = null, long? userId = null, bool fallbackToDefault = true)
{// 获取指定 Name 的 SettingDefinevar settingDefinition = _settingDefinitionManager.GetSettingDefinition(name);// 判断该设置项的使用范围是否为 Userif (settingDefinition.Scopes.HasFlag(SettingScopes.User) && userId.HasValue){var settingValue = await GetSettingValueForUserOrNullAsync(new UserIdentifier(tenantId, userId.Value), name);if (settingValue != null){return settingValue.Value;}if (!fallbackToDefault){return null;}if (!settingDefinition.IsInherited){return settingDefinition.DefaultValue;}}// 判断该设置项的使用范围是否为 Tenantif (settingDefinition.Scopes.HasFlag(SettingScopes.Tenant) && tenantId.HasValue){var settingValue = await GetSettingValueForTenantOrNullAsync(tenantId.Value, name);if (settingValue != null){return settingValue.Value;}if (!fallbackToDefault){return null;}if (!settingDefinition.IsInherited){return settingDefinition.DefaultValue;}}// 判断该设置项的使用范围是否为 Applicationif (settingDefinition.Scopes.HasFlag(SettingScopes.Application)){var settingValue = await GetSettingValueForApplicationOrNullAsync(name);if (settingValue != null){return settingValue.Value;}if (!fallbackToDefault){return null;}}// 如果都没有定义,则返回默认的设置值return settingDefinition.DefaultValue;
}

这里又为每个判断内部封装了一个方法,这里以 GetSettingValueForApplicationOrNullAsync() 为例,转到其定义:

private async Task<SettingInfo> GetSettingValueForApplicationOrNullAsync(string name)
{return (await GetApplicationSettingsAsync()).GetOrDefault(name);
}private async Task<Dictionary<string, SettingInfo>> GetApplicationSettingsAsync()
{// 从缓存当中获取设置信息,如果不存在,则执行其工厂方法return await _applicationSettingCache.GetAsync(ApplicationSettingsCacheKey, async () =>{var dictionary = new Dictionary<string, SettingInfo>();// 从 ISettingStore 当中获取对应的 Value 值var settingValues = await SettingStore.GetAllListAsync(null, null);foreach (var settingValue in settingValues){dictionary[settingValue.Name] = settingValue;}return dictionary;});
}

1.3.3 ISettingDefinitionManager

这个管理器作用最开始已经说明了,就是单纯的获取到用户注册到 Providers 里面的 SettingDefinition

1.3.4 SettingProvider

SettingProvider 用于开发人员配置自己的配置项,所有的设置提供者只需要继承自本类,实现其 GetSettingDefinitions 方法即可。

1.3.5 ISettingStore

本类用于设置项值的存储,其本身并不做设置项的新增,仅仅是相同的名称的设置项,优先从 ISettingStore 当中进行获取,如果不存在的话,才会使用开发人员在 SettingProvider 定义的值。

Abp 项目默认的 DefaultConfigSettingStore 实现并不会进行任何实质性的操作,只有 Zero.Common 项目当中重新实现的 SettingStore 类才是针对这些设置的值进行了持久化操作。

2.扩展:Abp.MailKit 模块配置

如果要在 .NetCore 环境下面使用邮件发送的话,首先推荐的就是 MailKit 这个库,而 Abp 针对 MailKit 库封装了一个新的模块,叫做 Abp.MailKit ,只需要进行简单的设置就可以发送邮件了。

在需要使用的模块上面添加:

[DependsOn(typeof(AbpMailKitModule))]
public class TestModule : AbpModule
{// 其他代码
}

之后需要自己定义一个 SettingProvider 并且在里面做好 SMTP 发件服务器配置:

public class DevEmailSettings : SettingProvider
{public override IEnumerable<SettingDefinition> GetSettingDefinitions(SettingDefinitionProviderContext context){return new[]{// smtp 服务器地址new SettingDefiniion(EmailSettingNames.Smtp.Host, "smtpserver"),// smtp 用户名称new SettingDefinition(EmailSettingNames.Smtp.UserName, "yourusername"),// smtp 服务端口new SettingDefinition(EmailSettingNames.Smtp.Port, "25"),// smtp 用户密码new SettingDefinition(EmailSettingNames.Smtp.Password, "yourpassword"),// 发件人邮箱地址new SettingDefinition(EmailSettingNames.DefaultFromAddress, "youremailaddress"),// 是否启用默认验证new SettingDefinition(EmailSettingNames.Smtp.UseDefaultCredentials,"false")};}
}

然后在之前的模块预加载当中添加这个 Provider 到全局设置当中:

[DependsOn(typeof(AbpMailKitModule))]
public class TestModule : AbpModule
{public override void PreInitialize(){Configuration.Settings.Providers.Add<DevEmailSettings>();}
}

发送邮件十分简单,直接在需要使用的地方注入 IEmailSender 调用其 Send 或者 SendAsync 方法即可,下面是一个例子:

public class TestApplicationService : ApplicationService
{private readonly IEmailSender _emailSender;public TestApplicationService(IEmailSender emailSender){_emailSender = emailSender;}public Task TestMethod(){_emailSender.Send("xxxxxx@qq.com","无主题","测试正文",false);return Task.FromResult(0);}
}

3.点此跳转到总目录

转载于:https://www.cnblogs.com/myzony/p/9253122.html

[Abp 源码分析]五、系统设置相关推荐

  1. 【转】ABP源码分析五:ABP初始化全过程

    ABP在初始化阶段做了哪些操作,前面的四篇文章大致描述了一下. 为个更清楚的描述其脉络,做了张流程图以辅助说明.其中每一步都涉及很多细节,难以在一张图中全部表现出来.每一步的细节(会涉及到较多接口,类 ...

  2. [Abp 源码分析]多租户体系与权限验证

    点击上方蓝字关注我们 0.简介 承接上篇文章我们会在这篇文章详细解说一下 Abp 是如何结合 IPermissionChecker 与 IFeatureChecker 来实现一个完整的多租户系统的权限 ...

  3. 【转】ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  4. 【转】ABP源码分析四十五:ABP ZERO中的EntityFramework模块

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

  5. 【转】ABP源码分析三十五:ABP中动态WebAPI原理解析

    动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...

  6. 【转】ABP源码分析二十五:EventBus

    IEventData/EventData: 封装了EventData信息,触发event的源对象和时间 IEventBus/EventBus: 定义和实现了了一系列注册,注销和触发事件处理函数的方法. ...

  7. 【转】ABP源码分析十五:ABP中的实用扩展方法

    类名 扩展的类型 方法名 参数 作用 XmlNodeExtensions XmlNode GetAttributeValueOrNull attributeName Gets an   attribu ...

  8. ABP源码分析二十五:EventBus

    IEventData/EventData: 封装了EventData信息,触发event的源对象和时间 IEventBus/EventBus: 定义和实现了了一系列注册,注销和触发事件处理函数的方法. ...

  9. [Abp 源码分析]ASP.NET Core 集成

    点击上方蓝字关注我们 0. 简介 整个 Abp 框架最为核心的除了 Abp 库之外,其次就是 Abp.AspNetCore 库了.虽然 Abp 本身是可以用于控制台程序的,不过那样的话 Abp 就基本 ...

最新文章

  1. 调试Tomcat源码
  2. AS打开速度慢,AS项目导入慢,新建项目导入慢
  3. 在linux的weblogic上增加启动参数
  4. zsh和bash的切换,默认shell,alias拼接组合多条命令
  5. shell语法 06-Linux文本处理-grep
  6. Barra 结构化风险模型实现(1)——沪深300指数的风格因子暴露度分析
  7. QQ网页登陆密码加密方式(农场、空间、WebQQ等通用)
  8. C++中lib和dll解析
  9. 5月16日上午学习日志
  10. Spring Data说明
  11. 如何测试WEB应用程序防止SQL注入***
  12. php强制浏览器不缓存,php强制浏览器不缓存和设置浏览器缓存
  13. 基于OpenCV及Python的数独问题识别与求解(一)图像预处理
  14. Servlet基础:容器
  15. 怎么换证件照底色?分享两款好用的免费制作证件照的软件
  16. 巴比特 | 元宇宙每日必读:数字藏品二级市场乱象丛生,00后成新韭菜,监管迫在眉睫?...
  17. 第12章 Spring AOP之扩展篇
  18. 【开发日常】【Makefile】编译时如何将警告(warning)视为错误(error)?
  19. docker 创建 Carte 服务
  20. Revit中如何绘制轴线?CAD图纸转轴网操作

热门文章

  1. 木棒围正方形c语言,三年级数学思维训练 (1)
  2. 从C语言快速过渡到C++
  3. Blackhole引擎(黑洞引擎)
  4. 深挖消费者需求,海天酱油多元化酿造
  5. IT小学员的避雷手册1--学习心态篇
  6. select下拉框箭头样式修改
  7. 用C语言模拟实现字符函数与字符串函数
  8. python中 .py .pyc .pyw .pyo .pyd各后缀文件区别
  9. 无参考图像单张视频图像噪声检测C++ opencv
  10. 一名优秀的HR需要具备哪些素质与能力?