ABP Settings
ABP Settings
2023/6/1
➡️

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 配置类, 在 DefinitionProviders 和 ValueProviders 里加入自己模块的 SettingDefinitionProvider 及 SettingValueProvider


//此对象是单例的
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 实现

有五个预构建设置值提供程序按以下顺序注册:

  1. DefaultValueSettingValueProvider: 从设置定义的默认值中获取值(参见上面的SettingDefinition部分).
  2. ConfigurationSettingValueProvider: 从IConfiguration服务中获取值.
  3. GlobalSettingValueProvider: 获取设置的全局(系统范围)值.
  4. TenantSettingValueProvider: 获取当前租户的设置值(参阅 多租户文档).
  5. 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);
    }
}
👍🎉🎊