Volo.Abp.Settings
AbpSettingsModule
AbpSettingsModule 加入所有的 ISettingValueProvider, ISettingDefinitionProvider 到 AbpSettingOptions 里
namespace Volo.Abp.Settings;
[DependsOn(
typeof(AbpLocalizationAbstractionsModule),
typeof(AbpSecurityModule),
typeof(AbpDataModule)
)]
public class AbpSettingsModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
// 自动添加 ISettingValueProvider 实现到 AbpSettingOptions
AutoAddDefinitionProviders(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
//注入 abp 内置的设置提供程序 ISettingValueProvider,这里少了租户的
// Volo.Abp.MultiTenancy 的 AbpMultiTenancyModule 模块里添加的
Configure<AbpSettingOptions>(options =>
{
options.ValueProviders.Add<DefaultValueSettingValueProvider>();
options.ValueProviders.Add<ConfigurationSettingValueProvider>();
options.ValueProviders.Add<GlobalSettingValueProvider>();
options.ValueProviders.Add<UserSettingValueProvider>();
});
}
// 自动添加 ISettingDefinitionProvider 实现到 AbpSettingOptions
private static void AutoAddDefinitionProviders(IServiceCollection services)
{
var definitionProviders = new List<Type>();
services.OnRegistered(context =>
{
if (typeof(ISettingDefinitionProvider).IsAssignableFrom(
context.ImplementationType))
{
definitionProviders.Add(context.ImplementationType);
}
});
//用户自定义 ISettingValueProvider 加入 AbpSettingOptions
services.Configure<AbpSettingOptions>(options =>
{
options.DefinitionProviders.AddIfNotContains(definitionProviders);
});
}
}
AbpSettingOptions 持有所有相关的核心类
其他模块需要参与到配置里 ,就需要把提供器给到 IOptions
//此对象是单例的
public class AbpSettingOptions
{
//设置值定义类型
public ITypeList<ISettingDefinitionProvider> DefinitionProviders { get; }
//设置值提供器类型
public ITypeList<ISettingValueProvider> ValueProviders { get; }
//删除的设置值
public HashSet<string> DeletedSettings { get; }
public AbpSettingOptions()
{
DefinitionProviders = new TypeList<ISettingDefinitionProvider>();
ValueProviders = new TypeList<ISettingValueProvider>();
DeletedSettings = new HashSet<string>();
}
}
SettingDefinition 设置定义
public class SettingDefinition
{
//设置的 key
public string Name { get; }
//本地化的展示名
public ILocalizableString DisplayName {
get => _displayName;
set => _displayName = Check.NotNull(value, nameof(value));
}
private ILocalizableString _displayName = default!;
public ILocalizableString? Description { get; set; }
//默认值,实际值要通过值提供器获取
public string? DefaultValue { get; set; }
public bool IsVisibleToClients { get; set; }
//对值提供程序进行过滤,只有允许的值提供程序才能提供值
//如果为空,所有的值提供程序都参与
public List<string> Providers { get; }
//是否允许继承
public bool IsInherited { get; set; }
//添加的额外属性
public Dictionary<string, object> Properties { get; }
//提供的值是否是加密的
public bool IsEncrypted { get; set; }
public SettingDefinition(
string name,
string? defaultValue = null,
ILocalizableString? displayName = null,
ILocalizableString? description = null,
bool isVisibleToClients = false,
bool isInherited = true,
bool isEncrypted = false)
{
Name = name;
DefaultValue = defaultValue;
IsVisibleToClients = isVisibleToClients;
DisplayName = displayName ?? new FixedLocalizableString(name);
Description = description;
IsInherited = isInherited;
IsEncrypted = isEncrypted;
Properties = new Dictionary<string, object>();
Providers = new List<string>();
}
public virtual SettingDefinition WithProperty(string key, object value)
{
Properties[key] = value;
return this;
}
public virtual SettingDefinition WithProviders(params string[] providers)
{
if (!providers.IsNullOrEmpty())
{
Providers.AddIfNotContains(providers);
}
return this;
}
}
ISettingValueProvider 值提供器
设置值提供程序,此接口表明设置的值要用key , value 的形式提供,并且允许进行嵌套 ISettingValueProvider 的实现类要做到根据 SettingDefinition 的定义返回一个字符串或一个字符串类型 key-value 集合
public interface ISettingValueProvider
{
// 值提供程序的名称,用来唯一的表示一个值提供程序
string Name { get; }
// 通过提供 SettingDefinition 参数获取 value
Task<string?> GetOrNullAsync([NotNull] SettingDefinition setting);
// 通过提供 SettingDefinition[] 参数获 取 key value 的集合
Task<List<SettingValue>> GetAllAsync([NotNull] SettingDefinition[] settings);
}
ISettingStore ISettingValueProvider 里 value 保存在 Store 里
public interface ISettingStore
{
Task<string?> GetOrNullAsync(
[NotNull] string name,
string? providerName,
//单独的租户 id
string? providerKey
);
Task<List<SettingValue>> GetAllAsync(
[NotNull] string[] names,
string? providerName,
string? providerName
);
}
null 实现
如果有实现就不注入了
[Dependency(TryRegister = true)]
public class NullSettingStore : ISettingStore, ISingletonDependency
{
public ILogger<NullSettingStore> Logger { get; set; }
public NullSettingStore()
{
Logger = NullLogger<NullSettingStore>.Instance;
}
public Task<string?> GetOrNullAsync(string name, string? providerName,
string? providerKey)
{
return Task.FromResult((string?)null);
}
public Task<List<SettingValue>> GetAllAsync(string[] names, string?
providerName, string? providerKey)
{
return Task.FromResult(names.Select(x => new SettingValue(x, null)).ToList());
}
}
通过数据库 ISettingManagementStore 管理设置值
namespace Volo.Abp.SettingManagement;
public class SettingStore : ISettingStore, ITransientDependency
{
protected ISettingManagementStore ManagementStore { get; }
public SettingStore(ISettingManagementStore managementStore)
{
ManagementStore = managementStore;
}
public virtual Task<string> GetOrNullAsync(string name,
string providerName, string providerKey)
{
return ManagementStore.GetOrNullAsync(name, providerName, providerKey);
}
public virtual Task<List<SettingValue>> GetAllAsync(string[] names,
string providerName, string providerKey)
{
return ManagementStore.GetListAsync(names, providerName, providerKey);
}
}
ISettingManagementStore 设置的值管理器
namespace Volo.Abp.SettingManagement;
public interface ISettingManagementStore
{
Task<string> GetOrNullAsync(string name, string providerName, string providerKey);
Task<List<SettingValue>> GetListAsync(string providerName, string providerKey);
Task<List<SettingValue>>
GetListAsync(string[] names, string providerName, string providerKey);
Task SetAsync(string name, string value, string providerName, string providerKey);
Task DeleteAsync(string name, string providerName, string providerKey);
}
public class SettingManagementStore : ISettingManagementStore, ITransientDependency
{
protected IDistributedCache<SettingCacheItem> Cache { get; }
protected ISettingDefinitionManager SettingDefinitionManager { get; }
protected ISettingRepository SettingRepository { get; }
protected IGuidGenerator GuidGenerator { get; }
public SettingManagementStore(
//设置值的仓库
ISettingRepository settingRepository,
IGuidGenerator guidGenerator,
//设置的值缓存
IDistributedCache<SettingCacheItem> cache,
ISettingDefinitionManager settingDefinitionManager)
{
SettingRepository = settingRepository;
GuidGenerator = guidGenerator;
Cache = cache;
SettingDefinitionManager = settingDefinitionManager;
}
//余下方法是数据库增删改查和缓存的过程
//这里之所以增加 UnitOfWork 标记 ,是因为默认 Get 方法,为了性能考虑,不在工作单元控制内
[UnitOfWork]
public virtual async Task<string> GetOrNullAsync(string name,
string providerName, string providerKey)
{
return (await GetCacheItemAsync(name, providerName, providerKey)).Value;
}
//核心方法,查询数据库获取设置值,并更新缓存
private async Task SetCacheItemsAsync(
string providerName,
string providerKey,
string currentName,
SettingCacheItem currentCacheItem)
{
// 获取所有的 settingDefinition (静态的动态的)
var settingDefinitions = await SettingDefinitionManager.GetAllAsync();
//获取设置值,需要提过设置器 name ,providerKey 一般为用户 id ,或租户 id
var settingsDictionary = (await SettingRepository.GetListAsync(
providerName, providerKey))
.ToDictionary(s => s.Name, s => s.Value);
var cacheItems = new List<KeyValuePair<string, SettingCacheItem>>();
//在所有设置定义里
foreach (var settingDefinition in settingDefinitions)
{
//获取设置的值
var settingValue = settingsDictionary.GetOrDefault(settingDefinition.Name);
//加入缓存的值
cacheItems.Add(
new KeyValuePair<string, SettingCacheItem>(
CalculateCacheKey(settingDefinition.Name, providerName, providerKey),
new SettingCacheItem(settingValue)
)
);
//如果定义是当前的查询
if (settingDefinition.Name == currentName)
{
//直接设置缓存值
currentCacheItem.Value = settingValue;
}
}
//
await Cache.SetManyAsync(cacheItems, considerUow: true);
}
}
SettingValueProvider 抽象方法规定 ISettingValueProvider 要在 ISettingStore 里获取设置值
namespace Volo.Abp.Settings;
public abstract class SettingValueProvider : ISettingValueProvider, ITransientDependency
{
public abstract string Name { get; }
protected ISettingStore SettingStore { get; }
//必须要传入 ISettingStore,意味者设置值保存在 ISettingStore 里
protected SettingValueProvider(ISettingStore settingStore)
{
SettingStore = settingStore;
}
public abstract Task<string?> GetOrNullAsync(SettingDefinition setting);
public abstract Task<List<SettingValue>> GetAllAsync(SettingDefinition[] settings);
}
abp 内置的 ISettingValueProvider 实现
有五个预构建设置值提供程序按以下顺序注册:
- DefaultValueSettingValueProvider: 从设置定义的默认值中获取值(参见上面的SettingDefinition部分).
- ConfigurationSettingValueProvider: 从IConfiguration服务中获取值.
- GlobalSettingValueProvider: 获取设置的全局(系统范围)值.
- TenantSettingValueProvider: 获取当前租户的设置值(参阅 多租户文档).
- UserSettingValueProvider: 获取当前用户的设置值(参阅 当前用户 文档).
设置回退系统从底部 (用户) 到 顶部(默认) 方向起作用.查找的时候先从底部开始,找不到往上找, 最后还找不到就使用默认值
ConfigurationSettingValueProvider ProviderName = "C"
ConfigurationSettingValueProvider 保存在 json 文件里,走 asp.net core 那一套,要求配置文件里的主 key 是 Settings 为了区别于其他值设置,abp 的设置值要保存在 Settings 这个 key 里,这个实现类的值保存在json 文件里,故没有继承自 SettingValueProvider
public class ConfigurationSettingValueProvider : ISettingValueProvider, ITransientDependency
{
public const string ConfigurationNamePrefix = "Settings:";
public const string ProviderName = "C";
public string Name => ProviderName;
protected IConfiguration Configuration { get; }
public ConfigurationSettingValueProvider(IConfiguration configuration)
{
Configuration = configuration;
}
//通过 IConfiguration 获取值
public virtual Task<string?> GetOrNullAsync(SettingDefinition setting)
{
return Task.FromResult(Configuration[ConfigurationNamePrefix + setting.Name]);
}
public Task<List<SettingValue>> GetAllAsync(SettingDefinition[] settings)
{
return Task.FromResult(settings.Select(x => new SettingValue(x.Name,
Configuration[ConfigurationNamePrefix + x.Name])).ToList());
}
}
DefaultValueSettingValueProvider ProviderName = "D"
获取缺省值
namespace Volo.Abp.Settings;
public class DefaultValueSettingValueProvider : SettingValueProvider
{
public const string ProviderName = "D";
public override string Name => ProviderName;
public DefaultValueSettingValueProvider(ISettingStore settingStore)
: base(settingStore)
{
}
//缺省值是可空类型
public override Task<string?> GetOrNullAsync(SettingDefinition setting)
{
return Task.FromResult(setting.DefaultValue);
}
public override Task<List<SettingValue>> GetAllAsync(SettingDefinition[] settings)
{
return Task.FromResult(settings.Select(x => new SettingValue(
x.Name, x.DefaultValue)).ToList());
}
}
GlobalSettingValueProvider ProviderName = "G"; 数据库获取
GlobalSettingValueProvider 全局设置
public class GlobalSettingValueProvider : SettingValueProvider
{
public const string ProviderName = "G";
public override string Name => ProviderName;
public GlobalSettingValueProvider(ISettingStore settingStore)
: base(settingStore)
{
}
//通过 SettingStore 获取
public override Task<string?> GetOrNullAsync(SettingDefinition setting)
{
return SettingStore.GetOrNullAsync(setting.Name, Name, null);
}
// 通过 SettingStore 获取
public override Task<List<SettingValue>> GetAllAsync(SettingDefinition[] settings)
{
return SettingStore.GetAllAsync(settings.Select(x => x.Name).ToArray(), Name, null);
}
}
TenantSettingValueProvider ProviderName = "T" 数据库获取
TenantSettingValueProvider 租户级别的设置,不同的设置 key,不同租户,有不同的 value
namespace Volo.Abp.Settings;
public class TenantSettingValueProvider : SettingValueProvider
{
public const string ProviderName = "T";
public override string Name => ProviderName;
protected ICurrentTenant CurrentTenant { get; }
public TenantSettingValueProvider(
ISettingStore settingStore, ICurrentTenant currentTenant)
: base(settingStore)
{
CurrentTenant = currentTenant;
}
//传入租户 id
public async override Task<string?> GetOrNullAsync(SettingDefinition setting)
{
return await SettingStore.GetOrNullAsync(
setting.Name, Name, CurrentTenant.Id?.ToString());
}
public override async Task<List<SettingValue>> GetAllAsync(SettingDefinition[] settings)
{
return await SettingStore.GetAllAsync(
settings.Select(x => x.Name).ToArray(), Name, CurrentTenant.Id?.ToString());
}
}
UserSettingValueProvider ProviderName = "U" 数据库获取
UserSettingValueProvider 当前用户级别的配置
namespace Volo.Abp.Settings;
public class UserSettingValueProvider : SettingValueProvider
{
public const string ProviderName = "U";
public override string Name => ProviderName;
protected ICurrentUser CurrentUser { get; }
public UserSettingValueProvider(ISettingStore settingStore, ICurrentUser currentUser)
: base(settingStore)
{
CurrentUser = currentUser;
}
//不同的用户 id ,区分不同的值
public override async Task<string?> GetOrNullAsync(SettingDefinition setting)
{
if (CurrentUser.Id == null)
{
return null;
}
return await SettingStore.GetOrNullAsync(
setting.Name, Name, CurrentUser.Id.ToString());
}
public override async Task<List<SettingValue>> GetAllAsync(SettingDefinition[] settings)
{
if (CurrentUser.Id == null)
{
return settings.Select(x => new SettingValue(x.Name, null)).ToList();
}
return await SettingStore.GetAllAsync(
settings.Select(x => x.Name).ToArray(), Name, CurrentUser.Id.ToString());
}
}
ISettingValueProviderManager 在DI 容器里获取所有的 ISettingValueProvider
public interface ISettingValueProviderManager
{
List<ISettingValueProvider> Providers { get; }
}
// 通过 AbpSettingOptions ValueProviders 获取的
public class SettingValueProviderManager :
ISettingValueProviderManager, ISingletonDependency
{
public List<ISettingValueProvider> Providers => _lazyProviders.Value;
protected AbpSettingOptions Options { get; }
private readonly Lazy<List<ISettingValueProvider>> _lazyProviders;
public SettingValueProviderManager(
IServiceProvider serviceProvider,
IOptions<AbpSettingOptions> options)
{
Options = options.Value;
_lazyProviders = new Lazy<List<ISettingValueProvider>>(
() => Options
.ValueProviders
.Select(type => serviceProvider.
GetRequiredService(type) as ISettingValueProvider)
.ToList()!,
//线程安全
true
);
}
}
ISettingDefinitionProvider 设置定义提供器
ISettingDefinitionContext 上下文里持有所有的设置定义
public interface ISettingDefinitionContext
{
SettingDefinition? GetOrNull(string name);
IReadOnlyList<SettingDefinition> GetAll();
void Add(params SettingDefinition[] definitions);
}
public class SettingDefinitionContext : ISettingDefinitionContext
{
protected Dictionary<string, SettingDefinition> Settings { get; }
public SettingDefinitionContext(Dictionary<string, SettingDefinition> settings)
{
Settings = settings;
}
public virtual SettingDefinition? GetOrNull(string name)
{
return Settings.GetOrDefault(name);
}
public virtual IReadOnlyList<SettingDefinition> GetAll()
{
return Settings.Values.ToImmutableList();
}
public virtual void Add(params SettingDefinition[] definitions)
{
if (definitions.IsNullOrEmpty())
{
return;
}
foreach (var definition in definitions)
{
Settings[definition.Name] = definition;
}
}
}
ISettingDefinitionProvider 开发给用户添加定义的接口
设置定义在可以在这里进行添加或修改存在的 ISettingDefinitionProvider 用户自定义 ISettingDefinitionProvider 继承 SettingDefinitionProvider 这样方便注入
用户定义的 SettingDefinition 通过 ISettingDefinitionProvider,最后被加入 ISettingDefinitionContext 上下文里
public interface ISettingDefinitionProvider
{
void Define(ISettingDefinitionContext context);
}
public abstract class SettingDefinitionProvider : ISettingDefinitionProvider,
ITransientDependency
{
//在此方法里通过 ISettingDefinitionContext 加入 SettingDefinition 到上下文里
public abstract void Define(ISettingDefinitionContext context);
}
IStaticSettingDefinitionStore 静态获取 通过 AbpSettingOptions 获取 SettingDefinition
因为有 Define 的 过程,这里使用了懒加载
public interface IStaticSettingDefinitionStore
{
Task<SettingDefinition> GetAsync([NotNull] string name);
Task<IReadOnlyList<SettingDefinition>> GetAllAsync();
Task<SettingDefinition?> GetOrNullAsync([NotNull] string name);
}
//单例
public class StaticSettingDefinitionStore : IStaticSettingDefinitionStore,
ISingletonDependency
{
protected Lazy<IDictionary<string, SettingDefinition>> SettingDefinitions { get; }
protected AbpSettingOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
// 传入 AbpSettingOptions 获取 SettingDefinition 定义
public StaticSettingDefinitionStore(IOptions<AbpSettingOptions> options,
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
Options = options.Value;
//不会立即求值
SettingDefinitions = new Lazy<IDictionary<string,
SettingDefinition>>(CreateSettingDefinitions, true);
}
public virtual async Task<SettingDefinition> GetAsync(string name)
{
Check.NotNull(name, nameof(name));
var setting = await GetOrNullAsync(name);
if (setting == null)
{
throw new AbpException("Undefined setting: " + name);
}
return setting;
}
public virtual Task<IReadOnlyList<SettingDefinition>> GetAllAsync()
{
return Task.FromResult<IReadOnlyList<SettingDefinition>>(
SettingDefinitions.Value.Values.ToImmutableList());
}
public virtual Task<SettingDefinition?> GetOrNullAsync(string name)
{
return Task.FromResult(SettingDefinitions.Value.GetOrDefault(name));
}
//来加载加载 SettingDefinition 的工厂
protected virtual IDictionary<string, SettingDefinition> CreateSettingDefinitions()
{
var settings = new Dictionary<string, SettingDefinition>();
using (var scope = ServiceProvider.CreateScope())
{
//获取所有的注入系统的 ISettingDefinitionProvider
//在 AbpSettingsModule 模块里的 AbpSettingOptions
//里 获取 所有的 DefinitionProviders
var providers = Options
//AbpSettingOptions 持有的 DefinitionProviders 类
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as
ISettingDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
//执行在 Define 获取所有 ISettingDefinitionProvider
// 提供的 SettingDefinition 到 settings 字典里
provider?.Define(new SettingDefinitionContext(settings));
}
}
//执行完上面的循环所有的设置定义 SettingDefinition ,就保存到
//SettingDefinitionContext 里的 settings 字典里
return settings;
}
}
IDynamicSettingDefinitionStore 获取 SettingDefinition ,
public interface IDynamicSettingDefinitionStore
{
Task<SettingDefinition> GetAsync([NotNull] string name);
Task<IReadOnlyList<SettingDefinition>> GetAllAsync();
Task<SettingDefinition?> GetOrNullAsync([NotNull] string name);
}
// Null 实现
public class NullDynamicSettingDefinitionStore :
IDynamicSettingDefinitionStore, ISingletonDependency
{
private readonly static Task<SettingDefinition?>
CachedNullableSettingResult = Task.FromResult((SettingDefinition?)null);
private readonly static Task<SettingDefinition>
CachedSettingResult = Task.FromResult((SettingDefinition)null!);
private readonly static Task<IReadOnlyList<SettingDefinition>>
CachedSettingsResult =
Task.FromResult((IReadOnlyList<SettingDefinition>)Array.
Empty<SettingDefinition>().ToImmutableList());
public Task<SettingDefinition> GetAsync(string name)
{
return CachedSettingResult;
}
public Task<IReadOnlyList<SettingDefinition>> GetAllAsync()
{
return CachedSettingsResult;
}
public Task<SettingDefinition?> GetOrNullAsync(string name)
{
return CachedNullableSettingResult;
}
}
SettingManagement模块实现的动态 IDynamicSettingDefinitionStore 接口
namespace Volo.Abp.SettingManagement;
public class DynamicSettingDefinitionStore : IDynamicSettingDefinitionStore,
ITransientDependency
{
protected ISettingDefinitionRecordRepository SettingRepository { get; }
protected ISettingDefinitionSerializer SettingDefinitionSerializer { get; }
protected IDynamicSettingDefinitionStoreInMemoryCache StoreCache { get; }
protected IDistributedCache DistributedCache { get; }
protected IAbpDistributedLock DistributedLock { get; }
public SettingManagementOptions SettingManagementOptions { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
public DynamicSettingDefinitionStore(
ISettingDefinitionRecordRepository textSettingRepository,
ISettingDefinitionSerializer textSettingDefinitionSerializer,
IDynamicSettingDefinitionStoreInMemoryCache storeCache,
IDistributedCache distributedCache,
IOptions<AbpDistributedCacheOptions> cacheOptions,
IOptions<SettingManagementOptions> settingManagementOptions,
IAbpDistributedLock distributedLock)
{
SettingRepository = textSettingRepository;
SettingDefinitionSerializer = textSettingDefinitionSerializer;
StoreCache = storeCache;
DistributedCache = distributedCache;
DistributedLock = distributedLock;
SettingManagementOptions = settingManagementOptions.Value;
CacheOptions = cacheOptions.Value;
}
public virtual async Task<SettingDefinition> GetAsync(string name)
{
var setting = await GetOrNullAsync(name);
if (setting == null)
{
throw new AbpException("Undefined setting: " + name);
}
return setting;
}
public virtual async Task<SettingDefinition> GetOrNullAsync(string name)
{
if (!SettingManagementOptions.IsDynamicSettingStoreEnabled)
{
return null;
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetSettingOrNull(name);
}
}
public virtual async Task<IReadOnlyList<SettingDefinition>> GetAllAsync()
{
if (!SettingManagementOptions.IsDynamicSettingStoreEnabled)
{
return Array.Empty<SettingDefinition>();
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetSettings().ToImmutableList();
}
}
protected virtual async Task EnsureCacheIsUptoDateAsync()
{
if (StoreCache.LastCheckTime.HasValue &&
DateTime.Now.Subtract(StoreCache.LastCheckTime.Value).TotalSeconds < 30)
{
/* We get the latest setting with a small delay for optimization */
return;
}
var stampInDistributedCache = await GetOrSetStampInDistributedCache();
if (stampInDistributedCache == StoreCache.CacheStamp)
{
StoreCache.LastCheckTime = DateTime.Now;
return;
}
await UpdateInMemoryStoreCache();
StoreCache.CacheStamp = stampInDistributedCache;
StoreCache.LastCheckTime = DateTime.Now;
}
protected virtual async Task UpdateInMemoryStoreCache()
{
var settingRecords = await SettingRepository.GetListAsync();
await StoreCache.FillAsync(settingRecords);
}
protected virtual async Task<string> GetOrSetStampInDistributedCache()
{
var cacheKey = GetCommonStampCacheKey();
var stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
await using (var commonLockHandle = await DistributedLock.
TryAcquireAsync(GetCommonDistributedLockKey(), TimeSpan.FromMinutes(2)))
{
if (commonLockHandle == null)
{
/* This request will fail */
throw new AbpException(
"Could not acquire distributed lock for setting definition common stamp check!"
);
}
stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
stampInDistributedCache = Guid.NewGuid().ToString();
await DistributedCache.SetStringAsync(
cacheKey,
stampInDistributedCache,
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromDays(30) //TODO: Make it configurable?
}
);
}
return stampInDistributedCache;
}
protected virtual string GetCommonStampCacheKey()
{
return $"{CacheOptions.KeyPrefix}_AbpInMemorySettingCacheStamp";
}
protected virtual string GetCommonDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_Common_AbpSettingUpdateLock";
}
}
ISettingDefinitionManager 获取静态,动态的 SettingDefinition
public interface ISettingDefinitionManager
{
[ItemNotNull]
Task<SettingDefinition> GetAsync([NotNull] string name);
[ItemNotNull]
Task<IReadOnlyList<SettingDefinition>> GetAllAsync();
[ItemCanBeNull]
Task<SettingDefinition?> GetOrNullAsync([NotNull] string name);
}
//通过 IStaticSettingDefinitionStore , IDynamicSettingDefinitionStore
//获取 SettingDefinition
public class SettingDefinitionManager : ISettingDefinitionManager, ISingletonDependency
{
protected readonly IStaticSettingDefinitionStore StaticStore;
protected readonly IDynamicSettingDefinitionStore DynamicStore;
public SettingDefinitionManager(IStaticSettingDefinitionStore staticStore,
IDynamicSettingDefinitionStore dynamicStore)
{
StaticStore = staticStore;
DynamicStore = dynamicStore;
}
public virtual async Task<SettingDefinition> GetAsync(string name)
{
var setting = await GetOrNullAsync(name);
if (setting == null)
{
throw new AbpException("Undefined setting: " + name);
}
return setting;
}
public virtual async Task<SettingDefinition?> GetOrNullAsync(string name)
{
Check.NotNull(name, nameof(name));
//先静态获取,静态获取不成功在动态获取
return await StaticStore.GetOrNullAsync(name) ?? await DynamicStore.
GetOrNullAsync(name);
}
public virtual async Task<IReadOnlyList<SettingDefinition>> GetAllAsync()
{
var staticSettings = await StaticStore.GetAllAsync();
var staticSettingNames = staticSettings
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicSettings = await DynamicStore.GetAllAsync();
/* We prefer static settings over dynamics */
//获取所有的静态合并动态的
return staticSettings.Concat(dynamicSettings.Where(d =>
!staticSettingNames.Contains(d.Name))).ToImmutableList();
}
}
ISettingEncryptionService 加解密设置值的 value
保存的 value 处于保密,会进行加密,ISettingEncryptionService 加解密服务
namespace Volo.Abp.Settings;
public interface ISettingEncryptionService
{
string? Encrypt([NotNull] SettingDefinition settingDefinition, string? plainValue);
string? Decrypt([NotNull] SettingDefinition settingDefinition, string? encryptedValue);
}
public class SettingEncryptionService : ISettingEncryptionService, ITransientDependency
{
protected IStringEncryptionService StringEncryptionService { get; }
public ILogger<SettingEncryptionService> Logger { get; set; }
public SettingEncryptionService(IStringEncryptionService stringEncryptionService)
{
StringEncryptionService = stringEncryptionService;
Logger = NullLogger<SettingEncryptionService>.Instance;
}
// plainValue 传入 set value
public virtual string? Encrypt(SettingDefinition settingDefinition, string? plainValue)
{
if (plainValue.IsNullOrEmpty())
{
return plainValue;
}
return StringEncryptionService.Encrypt(plainValue);
}
// encryptedValue 传入 set value
public virtual string? Decrypt(SettingDefinition settingDefinition, string? encryptedValue)
{
if (encryptedValue.IsNullOrEmpty())
{
return encryptedValue;
}
try
{
return StringEncryptionService.Decrypt(encryptedValue);
}
catch (Exception e)
{
Logger.LogException(e);
return string.Empty;
}
}
}
ISettingProvider ,ISettingDefinitionManager ,ISettingValueProviderManager 的门面,用户主要的使用 api
默认的实现 通过 name 在 ISettingDefinitionManager 里找到 SettingDefinition 通过 SettingDefinition 里的 Providers 在 ISettingValueProviderManager 找到 ISettingValueProvider 最后通过 ISettingValueProvider 找到需要的值
为什么要这样做能,因为同一个key,会存在多个 ISettingValueProvider 通过 SettingProvider,才能做到这种查询 SettingProvider 更明显的像是一个门面提供给用户
ISettingProvider 是非常常用的服务,一些基类中(如IApplicationService)已经将其属性注入. 这种情况下可以直接使用SettingProvider.
namespace Volo.Abp.Settings;
public interface ISettingProvider
{
Task<string?> GetOrNullAsync([NotNull] string name);
Task<List<SettingValue>> GetAllAsync([NotNull] string[] names);
Task<List<SettingValue>> GetAllAsync();
}
//
public class SettingProvider : ISettingProvider, ITransientDependency
{
protected ISettingDefinitionManager SettingDefinitionManager { get; }
protected ISettingEncryptionService SettingEncryptionService { get; }
protected ISettingValueProviderManager SettingValueProviderManager { get; }
//通过注入这些服务找到匹配的值
public SettingProvider(
ISettingDefinitionManager settingDefinitionManager,
ISettingEncryptionService settingEncryptionService,
ISettingValueProviderManager settingValueProviderManager)
{
SettingDefinitionManager = settingDefinitionManager;
SettingEncryptionService = settingEncryptionService;
SettingValueProviderManager = settingValueProviderManager;
}
public virtual async Task<string?> GetOrNullAsync(string name)
{
//获取静态和动态的值定义
var setting = await SettingDefinitionManager.GetAsync(name);
//倒叙排列值提供器
var providers = Enumerable
.Reverse(SettingValueProviderManager.Providers);
//有需要过滤的值提供程序 s
if (setting.Providers.Any())
{
//在所有提供的值提供器里过滤指定的的 setting.Providers name 的过滤器
providers = providers.Where(p => setting.Providers.Contains(p.Name));
}
//获取值
var value = await GetOrNullValueFromProvidersAsync(providers, setting);
if (value != null && setting.IsEncrypted)
{
//如果值被加过密了,需要解密
value = SettingEncryptionService.Decrypt(setting, value);
}
return value;
}
//获取所有的指定 name 的 键值对设置值
public virtual async Task<List<SettingValue>> GetAllAsync(string[] names)
{
var result = new Dictionary<string, SettingValue>();
//所有的 settingDefinition
var settingDefinitions = (await SettingDefinitionManager.GetAllAsync()).
Where(x => names.Contains(x.Name)).ToList();
foreach (var definition in settingDefinitions)
{
//先设置 definition.Name 到 result
result.Add(definition.Name, new SettingValue(definition.Name, null));
}
//循环遍历所有的值提供程序
foreach (var provider in Enumerable.Reverse(SettingValueProviderManager.Providers))
{
//获取所有的值
var settingValues = await provider.GetAllAsync(settingDefinitions.
Where(x => !x.Providers.Any() || x.Providers.Contains(provider.Name)).ToArray());
var notNullValues = settingValues.Where(x => x.Value != null).ToList();
//
foreach (var settingValue in notNullValues)
{
var settingDefinition = settingDefinitions.First(
x => x.Name == settingValue.Name);
if (settingDefinition.IsEncrypted)
{
//对获取的值,进行解密
settingValue.Value = SettingEncryptionService.Decrypt(
settingDefinition, settingValue.Value);
}
if (result.ContainsKey(settingValue.Name) &&
result[settingValue.Name].Value == null)
{
//把通过值提供器设置的值赋值给 result
result[settingValue.Name].Value = settingValue.Value;
}
}
//排除所有获取到值的 settingDefinition
settingDefinitions.RemoveAll(x => notNullValues.Any(v => v.Name == x.Name));
//如果全部获取到了,就退出循环
if (!settingDefinitions.Any())
{
break;
}
}
return result.Values.ToList();
}
//获取所有的值
public virtual async Task<List<SettingValue>> GetAllAsync()
{
var settingValues = new List<SettingValue>();
var settingDefinitions = await SettingDefinitionManager.GetAllAsync();
foreach (var setting in settingDefinitions)
{
settingValues.Add(new SettingValue(setting.Name,
await GetOrNullAsync(setting.Name)));
}
return settingValues;
}
//在所有的值提供程序里匹配第一个满足的值
protected virtual async Task<string?> GetOrNullValueFromProvidersAsync(
IEnumerable<ISettingValueProvider> providers,
SettingDefinition setting)
{
// 值提供程序里找到第一个就退出
foreach (var provider in providers)
{
var value = await provider.GetOrNullAsync(setting);
if (value != null)
{
return value;
}
}
return null;
}
}
SettingProviderExtensions 扩展
public static class SettingProviderExtensions
{
public async static Task<bool> IsTrueAsync(
[NotNull] this ISettingProvider settingProvider, [NotNull] string name)
{
Check.NotNull(settingProvider, nameof(settingProvider));
Check.NotNull(name, nameof(name));
return string.Equals(
await settingProvider.GetOrNullAsync(name),
"true",
StringComparison.OrdinalIgnoreCase
);
}
public async static Task<T> GetAsync<T>(
[NotNull] this ISettingProvider settingProvider, [NotNull] string name,
T defaultValue = default)
where T : struct
{
Check.NotNull(settingProvider, nameof(settingProvider));
Check.NotNull(name, nameof(name));
var value = await settingProvider.GetOrNullAsync(name);
return value?.To<T>() ?? defaultValue;
}
}
SettingManagement 模块主要是为了实现设置 value 及设置定义的 store
Setting 设置值聚合
namespace Volo.Abp.SettingManagement;
public class Setting : Entity<Guid>, IAggregateRoot<Guid>
{
[NotNull]
public virtual string Name { get; protected set; }
[NotNull]
public virtual string Value { get; internal set; }
[CanBeNull]
public virtual string ProviderName { get; protected set; }
[CanBeNull]
public virtual string ProviderKey { get; protected set; }
protected Setting()
{
}
public Setting(
Guid id,
[NotNull] string name,
[NotNull] string value,
[CanBeNull] string providerName = null,
[CanBeNull] string providerKey = null)
{
Check.NotNull(name, nameof(name));
Check.NotNull(value, nameof(value));
Id = id;
Name = name;
Value = value;
ProviderName = providerName;
ProviderKey = providerKey;
}
public override string ToString()
{
return $"{base.ToString()}, Name = {Name},
Value = {Value}, ProviderName = {ProviderName}, ProviderKey = {ProviderKey}";
}
}
SettingDefinition 设置定义聚合
public class SettingDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraProperties
{
[NotNull]
public string Name { get; set; }
[NotNull]
public string DisplayName { get; set; }
[CanBeNull]
public string Description { get; set; }
[CanBeNull]
public string DefaultValue { get; set; }
public bool IsVisibleToClients { get; set; }
public string Providers { get; set; }
public bool IsInherited { get; set; }
public bool IsEncrypted { get; set; }
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public SettingDefinitionRecord()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public SettingDefinitionRecord(
Guid id,
string name,
string displayName,
string description,
string defaultValue,
bool isVisibleToClients,
string providers,
bool isInherited,
bool isEncrypted)
: base(id)
{
Name =
Check.NotNullOrWhiteSpace(
name, nameof(name), SettingDefinitionRecordConsts.MaxNameLength);
DisplayName =
Check.NotNullOrWhiteSpace(
displayName, nameof(displayName), SettingDefinitionRecordConsts.MaxDisplayNameLength);
Description =
Check.Length(
description, nameof(description), SettingDefinitionRecordConsts.MaxDescriptionLength);
DefaultValue
= Check.Length(
defaultValue, nameof(defaultValue), SettingDefinitionRecordConsts.MaxDefaultValueLength);
IsVisibleToClients = isVisibleToClients;
Providers =
Check.Length(
providers, nameof(providers), SettingDefinitionRecordConsts.MaxProvidersLength);
IsInherited = isInherited;
IsEncrypted = isEncrypted;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
}
经常使用的是此模块,此模块默认管理了 Emailing,及 AbpTimingModule 设置管理
namespace Volo.Abp.SettingManagement;
[DependsOn(
typeof(AbpDddApplicationModule),
typeof(AbpSettingManagementDomainModule),
typeof(AbpSettingManagementApplicationContractsModule),
typeof(AbpEmailingModule),
typeof(AbpTimingModule)
)]
public class AbpSettingManagementApplicationModule : AbpModule
{
}
namespace Volo.Abp.Emailing;
{
"culture": "zh-Hans",
"texts": {
"DisplayName:Abp.Mailing.DefaultFromAddress": "默认发件人地址",
"DisplayName:Abp.Mailing.DefaultFromDisplayName": "默认发件人名字",
"DisplayName:Abp.Mailing.Smtp.Host": "主机",
"DisplayName:Abp.Mailing.Smtp.Port": "端口",
"DisplayName:Abp.Mailing.Smtp.UserName": "用户名",
"DisplayName:Abp.Mailing.Smtp.Password": "密码",
"DisplayName:Abp.Mailing.Smtp.Domain": "域",
"DisplayName:Abp.Mailing.Smtp.EnableSsl": "启用SSL",
"DisplayName:Abp.Mailing.Smtp.UseDefaultCredentials": "使用默认凭据",
"Description:Abp.Mailing.DefaultFromAddress": "默认的发件人地址.",
"Description:Abp.Mailing.DefaultFromDisplayName": "默认的发件人名字.",
"Description:Abp.Mailing.Smtp.Host": "SMTP 事务的主机名或主机 IP 地址.",
"Description:Abp.Mailing.Smtp.Port": "SMTP 事务的端口.",
"Description:Abp.Mailing.Smtp.UserName": "凭据关联的用户名.",
"Description:Abp.Mailing.Smtp.Password": "凭据关联的用户名的密码.",
"Description:Abp.Mailing.Smtp.Domain": "验证凭据的域名或计算机名.",
"Description:Abp.Mailing.Smtp.EnableSsl": "指定 SmtpClient 是否使用安全套接字层 (SSL)
加密连接.",
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "控制默认凭据是否随请求一起发送.",
"TextTemplate:StandardEmailTemplates.Layout": "默认电子邮件模板",
"TextTemplate:StandardEmailTemplates.Message": "电子邮件的简单消息模板"
}
}
//资源伪类
[LocalizationResourceName("AbpEmailing")]
public class EmailingResource
{
}
//提供的设置定义
internal class EmailSettingProvider : SettingDefinitionProvider
{
public override void Define(ISettingDefinitionContext context)
{
context.Add(
new SettingDefinition(
EmailSettingNames.Smtp.Host,
"127.0.0.1",
L("DisplayName:Abp.Mailing.Smtp.Host"),
L("Description:Abp.Mailing.Smtp.Host")),
new SettingDefinition(EmailSettingNames.Smtp.Port,
"25",
L("DisplayName:Abp.Mailing.Smtp.Port"),
L("Description:Abp.Mailing.Smtp.Port")),
new SettingDefinition(
EmailSettingNames.Smtp.UserName,
displayName: L("DisplayName:Abp.Mailing.Smtp.UserName"),
description: L("Description:Abp.Mailing.Smtp.UserName")),
new SettingDefinition(
EmailSettingNames.Smtp.Password,
displayName:
L("DisplayName:Abp.Mailing.Smtp.Password"),
description: L("Description:Abp.Mailing.Smtp.Password"),
isEncrypted: true),
new SettingDefinition(
EmailSettingNames.Smtp.Domain,
displayName: L("DisplayName:Abp.Mailing.Smtp.Domain"),
description: L("Description:Abp.Mailing.Smtp.Domain")),
new SettingDefinition(
EmailSettingNames.Smtp.EnableSsl,
"false",
L("DisplayName:Abp.Mailing.Smtp.EnableSsl"),
L("Description:Abp.Mailing.Smtp.EnableSsl")),
new SettingDefinition(
EmailSettingNames.Smtp.UseDefaultCredentials,
"true",
L("DisplayName:Abp.Mailing.Smtp.UseDefaultCredentials"),
L("Description:Abp.Mailing.Smtp.UseDefaultCredentials")),
new SettingDefinition(
EmailSettingNames.DefaultFromAddress,
"noreply@abp.io",
L("DisplayName:Abp.Mailing.DefaultFromAddress"),
L("Description:Abp.Mailing.DefaultFromAddress")),
new SettingDefinition(EmailSettingNames.DefaultFromDisplayName,
"ABP application",
L("DisplayName:Abp.Mailing.DefaultFromDisplayName"),
L("Description:Abp.Mailing.DefaultFromDisplayName"))
);
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<EmailingResource>(name);
}
}
namespace Volo.Abp.Timing;
{
"culture": "zh-Hans",
"texts": {
"DisplayName:Abp.Timing.Timezone": "时区",
"Description:Abp.Timing.Timezone": "应用程序的时区"
}
}
//资源伪类
[LocalizationResourceName("AbpTiming")]
public class AbpTimingResource
{
}
public class TimingSettingProvider : SettingDefinitionProvider
{
public override void Define(ISettingDefinitionContext context)
{
context.Add(
new SettingDefinition(TimingSettingNames.TimeZone,
"UTC",
L("DisplayName:Abp.Timing.Timezone"),
L("Description:Abp.Timing.Timezone"),
isVisibleToClients: true)
);
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<AbpTimingResource>(name);
}
}