Abp 自己有兩種設置,一種就是 上一篇文章 所介紹的模塊配置 Configuration,該配置主要用於一些複雜的數據類型設置,不單單是字符串,也有多是一些 C# 運行時的一些變量。另一種則是本篇文章所講的 Setting,Setting 主要用於配置一些簡單的參數,好比 SMTP 地址,數據庫鏈接字符串等一些基本的配置類型可使用 Setting 來進行處理。html
咱們先來看一下設置是怎樣被加入到 Abp 框架當中,而且是如何來使用它的。數據庫
在 Abp 框架內部開發人員能夠經過 ISettingsConfiguration
的 Providers 屬性來添加本身實現的 SettingProvider
,而 ISettingsConfiguration
的初始化是在上一篇文章所寫的 AbpBootstrapper.Initialize()
裏面進行初始化的。緩存
開發人員經過繼承 SettingProvider
來提供這些設置信息,而且在模塊的 PreInitialize()
方法當中經過 Configuration
來添加書寫好的配置提供者。服務器
在模塊進行初始化以後(也就是在 PostInitiailze()
方法內部),全部開發人員定義的 SettingProvider
經過 ISettingDefinitionManager
的 Initialize()
方法存儲到一個 Dictionary
裏面。app
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
來進行管理的。async
全部的設置項是經過 ServiceProvider
來提供的。ide
設置的持久化配置則是經過 ISettingStore
來實現的,開發者能夠經過替換 ISettingStore
的實現達到持久化到數據庫或者是其餘位置。測試
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); } }
public sealed class AbpKernelModule : AbpModule { public override void PreInitialize() { // 其餘代碼 Configuration.Settings.Providers.Add<EmailSettingProvider>(); // 其餘代碼 } }
注入以後,那麼相應的模塊如何獲得已經注入的配置項呢?ui
咱們拿一個最直觀的例子來展現一下,這裏咱們來到 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()
就能夠拿到這些設置項。
Abp 系統設置相關的最核心的部分就是 ISettingManager
、ISettingDefinitionManager
、ISettingStore
,SettingProvider
、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; } } }
首先咱們看一下 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(); } }
能夠看到在這裏面,他注入了 ISetingStore
與 ISettingDefinitionManager
,而且使用了三個 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 的 SettingDefine var settingDefinition = _settingDefinitionManager.GetSettingDefinition(name); // 判斷該設置項的使用範圍是否爲 User if (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; } } // 判斷該設置項的使用範圍是否爲 Tenant if (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; } } // 判斷該設置項的使用範圍是否爲 Application if (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; }); }
這個管理器做用最開始已經說明了,就是單純的獲取到用戶註冊到 Providers 裏面的 SettingDefinition
。
SettingProvider 用於開發人員配置本身的配置項,全部的設置提供者只須要繼承自本類,實現其 GetSettingDefinitions
方法便可。
本類用於設置項值的存儲,其自己並不作設置項的新增,僅僅是相同的名稱的設置項,優先從 ISettingStore
當中進行獲取,若是不存在的話,纔會使用開發人員在 SettingProvider
定義的值。
Abp 項目默認的 DefaultConfigSettingStore
實現並不會進行任何實質性的操做,只有 Zero.Common 項目當中從新實現的 SettingStore
類纔是針對這些設置的值進行了持久化操做。
若是要在 .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); } }