美文网首页ABP
ABP 源码解析 八. 设置管理

ABP 源码解析 八. 设置管理

作者: 诸葛_小亮 | 来源:发表于2018-08-29 10:57 被阅读67次

    介绍

    此系列文章主要是对ABP源码进行解读,初探作者在创造ABP的框架思路,和使用到的设计模式进行。
    通过解读ABP源码,可以提升ABP使用方式,可以提升编码意识,提高面向对象编程思想。

    本篇文章主要介绍ABP框架的系统设置管理等机制。

    每个应用程序都需要存储一些设置,并在应用程序的某个地方使用这些设置。ABP提供了一个强大的基础结构来存储/检索服务器端和客户端(js)上可用的应用程序、租户和用户级别设置。
    设置是通常存储在数据库(或其他源)中的Name-Value字符串对。我们可以通过将非字符串值转换为字符串来存储。

    ABP提供了三种设置范围:

    • 应用级:应用程序范围的设置用于用户/租户独立的设置
    • 租户级:如果应用程序是多租户的,我们可以定义租户特定的设置
    • 用户级:我们可以使用用户范围的设置来存储/获取每个用户特定的设置值

    默认设置范围是分层的(除非您将IsInherited为false)。例如,如果我们将设置的范围定义为“应用程序租户用户”,并尝试获取设置的当前值,获取当前值的逻辑如下

    • 定义了用户级别的设置,优先使用用户级别的设置值
    • 如果没有定义用户级别的设置,但是定义了租户级别的设置值,那么使用租户级别的值
    • 如果即没有定义用户级别的设置,也没有定义租户级别的设置值,那么使用应用级别的值
    • 如果都没有定义,则使用默认值
    设置层

    参考:


    UML

    设置
    • SettingDefinition: 定义设置,设置用于配置和更改应用程序
    • SettingDefinitionGroup: 设置组用于将一些设置组合在一起, 一组可以是另一组的子组
    • ISettingDefinitionManager: 定义设置定义信息管理器
    • SettingDefinitionManager: 实现设置信息管理器,负责从提供者中读取设置信息
    • SettingDefinitionProviderContext:设置提供者所使用的上下文
    • ISettingsConfiguration: 配置设置系统,主要是提供设置提供者类型
    • SettingProvider: 设置提供者基类,继承这个类来定义模块/应用程序的设置
    • SettingScopes:设置范围,枚举类型,可以组合使用
    • ISettingManager: 这是必须实现的主要接口,以便能够加载/更改设置的值
    • SettingManager:该类实现了在数据库中管理设置值的方法
    • ISettingStore:定义用于从/到数据源(数据库)获取/设置,在Module.Zero中实现
    • ISettingValue:定义设置值接口
    • SettingValueObject:设置值的值对象

    源码解析

    定义设置

    定义SettingDefinition类,提供设置属性

    /// <summary>
        /// Defines a setting.
        /// A setting is used to configure and change behavior of the application.
        /// 定义设置,
        /// 设置用于配置和更改应用程序
        /// </summary>
        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"/>.
            /// 设置的范围,默认值是<see cref="SettingScopes.Application"/>
            /// </summary>
            public SettingScopes Scopes { get; set; }
    
            /// <summary>
            /// Is this setting inherited from parent scopes.
            /// Default: True.
            /// 设置是从父范围继承,默认为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; }
    
            /// <summary>
            /// Creates a new <see cref="SettingDefinition"/> object.
            /// </summary>
            /// <param name="name">Unique name of the setting</param>
            /// <param name="defaultValue">Default value of the setting</param>
            /// <param name="displayName">Display name of the permission</param>
            /// <param name="group">Group of this setting</param>
            /// <param name="description">A brief description for this setting</param>
            /// <param name="scopes">Scopes of this setting. Default value: <see cref="SettingScopes.Application"/>.</param>
            /// <param name="isVisibleToClients">Can clients see this setting and it's value. Default: false</param>
            /// <param name="isInherited">Is this setting inherited from parent scopes. Default: True.</param>
            /// <param name="customData">Can be used to store a custom object related to this setting</param>
            /// <param name="clientVisibilityProvider">Client visibility definition for the setting. Default: invisible</param>
            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;
                }
            }
        }
    

    设置提供者

    为了方便从各个模块中读取相应的设置信息,需要定义一个设置提供者,在模块中继承SettingProvider并实现,在模块启动中使用SettingsConfiguration设置具体的提供者类型

    /// <summary>
        /// Inherit this class to define settings for a module/application.
        /// 继承这个类来定义模块/应用程序的设置
        /// </summary>
        public abstract class SettingProvider : ITransientDependency
        {
            /// <summary>
            /// Gets all setting definitions provided by this provider.
            /// 获取由该提供者提供的所有设置定义
            /// </summary>
            /// <returns>List of settings</returns>
            public abstract IEnumerable<SettingDefinition> GetSettingDefinitions(SettingDefinitionProviderContext context);
        }
    
    Configuration.Settings.Providers.Add<MySettingProvider>();
    

    设置定义管理器

    管理器主要收集所有设置提供者提供的设置定义信息

    /// <summary>
        /// Implements <see cref="ISettingDefinitionManager"/>.
        /// 实现<see cref="ISettingDefinitionManager"/>,负责从提供者中读取设置信息
        /// </summary>
        internal class SettingDefinitionManager : ISettingDefinitionManager, ISingletonDependency
        {
    
            private readonly IIocManager _iocManager;
    
            /// <summary>
            /// 设置的配置
            /// </summary>
            private readonly ISettingsConfiguration _settingsConfiguration;
            private readonly IDictionary<string, SettingDefinition> _settings;
    
            /// <summary>
            /// Constructor.
            /// </summary>
            public SettingDefinitionManager(IIocManager iocManager, ISettingsConfiguration settingsConfiguration)
            {
                _iocManager = iocManager;
                _settingsConfiguration = settingsConfiguration;
                _settings = new Dictionary<string, SettingDefinition>();
            }
    
            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;
                        }
                    }
                }
            }
    
            public SettingDefinition GetSettingDefinition(string name)
            {
                if (!_settings.TryGetValue(name, out var settingDefinition))
                {
                    throw new AbpException("There is no setting defined with name: " + name);
                }
    
                return settingDefinition;
            }
    
            public IReadOnlyList<SettingDefinition> GetAllSettingDefinitions()
            {
                return _settings.Values.ToImmutableList();
            }
    
            private IDisposableDependencyObjectWrapper<SettingProvider> CreateProvider(Type providerType)
            {
                return _iocManager.ResolveAsDisposable<SettingProvider>(providerType);
            }
        }
    

    管理设置

    为了在其他地方使用修改和获取设置值,abp使用SettingManager实现
    该类主要提供三个大功能:

    • 获取设置值
    • 获取全部设置值
    • 更新设置值
    /// <summary>
        /// This class implements <see cref="ISettingManager"/> to manage setting values in the database.
        /// 该类实现了在数据库中管理设置值的方法。
        /// </summary>
        public class SettingManager : ISettingManager, ISingletonDependency
        {
            /// <summary>
            /// 缓存的key
            /// </summary>
            public const string ApplicationSettingsCacheKey = "ApplicationSettings";
    
            /// <summary>
            /// Reference to the current Session.
            /// 当前Session
            /// </summary>
            public IAbpSession AbpSession { get; set; }
    
            /// <summary>
            /// Reference to the setting store.
            /// 设置存储
            /// </summary>
            public ISettingStore SettingStore { get; set; }
    
            /// <summary>
            /// 设置定义管理器
            /// </summary>
            private readonly ISettingDefinitionManager _settingDefinitionManager;
    
    
            /// <summary>
            /// 应用级别设置缓存
            /// </summary>
            private readonly ITypedCache<string, Dictionary<string, SettingInfo>> _applicationSettingCache;
    
            /// <summary>
            /// 租户级别设置缓存
            /// </summary>
            private readonly ITypedCache<int, Dictionary<string, SettingInfo>> _tenantSettingCache;
    
            /// <summary>
            /// 用户级别设置缓存
            /// </summary>
            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();
            }
    
            #region Public methods
    
            /// <inheritdoc/>
            public Task<string> GetSettingValueAsync(string name)
            {
                return GetSettingValueInternalAsync(name, AbpSession.TenantId, AbpSession.UserId);
            }
    
            public Task<string> GetSettingValueForApplicationAsync(string name)
            {
                return GetSettingValueInternalAsync(name);
            }
    
            public Task<string> GetSettingValueForApplicationAsync(string name, bool fallbackToDefault)
            {
                return GetSettingValueInternalAsync(name, fallbackToDefault: fallbackToDefault);
            }
    
            public Task<string> GetSettingValueForTenantAsync(string name, int tenantId)
            {
                return GetSettingValueInternalAsync(name, tenantId);
            }
    
            public Task<string> GetSettingValueForTenantAsync(string name, int tenantId, bool fallbackToDefault)
            {
                return GetSettingValueInternalAsync(name, tenantId, fallbackToDefault: fallbackToDefault);
            }
    
            public Task<string> GetSettingValueForUserAsync(string name, int? tenantId, long userId)
            {
                return GetSettingValueInternalAsync(name, tenantId, userId);
            }
    
            public Task<string> GetSettingValueForUserAsync(string name, int? tenantId, long userId, bool fallbackToDefault)
            {
                return GetSettingValueInternalAsync(name, tenantId, userId, fallbackToDefault);
            }
    
            /// <summary>
            /// 获取全部设置值
            /// </summary>
            /// <returns></returns>
            public async Task<IReadOnlyList<ISettingValue>> GetAllSettingValuesAsync()
            {
                return await GetAllSettingValuesAsync(SettingScopes.Application | SettingScopes.Tenant | SettingScopes.User);
            }
            
            /// <summary>
            /// 获取全部设置值,用户级别覆盖租户级别,租户级别覆盖应用级别,应用级别覆盖默认值
            /// </summary>
            /// <param name="scopes"></param>
            /// <returns></returns>
            public async Task<IReadOnlyList<ISettingValue>> GetAllSettingValuesAsync(SettingScopes scopes)
            {
                var settingDefinitions = new Dictionary<string, SettingDefinition>();
                var settingValues = new Dictionary<string, ISettingValue>();
    
                //Fill all setting with default values.
                foreach (var setting in _settingDefinitionManager.GetAllSettingDefinitions())
                {
                    settingDefinitions[setting.Name] = setting;
                    settingValues[setting.Name] = new SettingValueObject(setting.Name, setting.DefaultValue);
                }
    
                //Overwrite application settings
                if (scopes.HasFlag(SettingScopes.Application))
                {
                    foreach (var settingValue in await GetAllSettingValuesForApplicationAsync())
                    {
                        var setting = settingDefinitions.GetOrDefault(settingValue.Name);
    
                        //TODO: Conditions get complicated, try to simplify it
                        if (setting == null || !setting.Scopes.HasFlag(SettingScopes.Application))
                        {
                            continue;
                        }
    
                        if (!setting.IsInherited &&
                            ((setting.Scopes.HasFlag(SettingScopes.Tenant) && AbpSession.TenantId.HasValue) || (setting.Scopes.HasFlag(SettingScopes.User) && AbpSession.UserId.HasValue)))
                        {
                            continue;
                        }
    
                        settingValues[settingValue.Name] = new SettingValueObject(settingValue.Name, settingValue.Value);
                    }
                }
    
                //Overwrite tenant settings
                if (scopes.HasFlag(SettingScopes.Tenant) && AbpSession.TenantId.HasValue)
                {
                    foreach (var settingValue in await GetAllSettingValuesForTenantAsync(AbpSession.TenantId.Value))
                    {
                        var setting = settingDefinitions.GetOrDefault(settingValue.Name);
    
                        //TODO: Conditions get complicated, try to simplify it
                        if (setting == null || !setting.Scopes.HasFlag(SettingScopes.Tenant))
                        {
                            continue;
                        }
    
                        if (!setting.IsInherited &&
                            (setting.Scopes.HasFlag(SettingScopes.User) && AbpSession.UserId.HasValue))
                        {
                            continue;
                        }
    
                        settingValues[settingValue.Name] = new SettingValueObject(settingValue.Name, settingValue.Value);
                    }
                }
    
                //Overwrite user settings
                if (scopes.HasFlag(SettingScopes.User) && AbpSession.UserId.HasValue)
                {
                    foreach (var settingValue in await GetAllSettingValuesForUserAsync(AbpSession.ToUserIdentifier()))
                    {
                        var setting = settingDefinitions.GetOrDefault(settingValue.Name);
                        if (setting != null && setting.Scopes.HasFlag(SettingScopes.User))
                        {
                            settingValues[settingValue.Name] = new SettingValueObject(settingValue.Name, settingValue.Value);
                        }
                    }
                }
    
                return settingValues.Values.ToImmutableList();
            }
    
            /// <inheritdoc/>
            public async Task<IReadOnlyList<ISettingValue>> GetAllSettingValuesForApplicationAsync()
            {
                return (await GetApplicationSettingsAsync()).Values
                    .Select(setting => new SettingValueObject(setting.Name, setting.Value))
                    .ToImmutableList();
            }
    
            /// <inheritdoc/>
            public async Task<IReadOnlyList<ISettingValue>> GetAllSettingValuesForTenantAsync(int tenantId)
            {
                return (await GetReadOnlyTenantSettings(tenantId)).Values
                    .Select(setting => new SettingValueObject(setting.Name, setting.Value))
                    .ToImmutableList();
            }
    
            /// <inheritdoc/>
            public Task<IReadOnlyList<ISettingValue>> GetAllSettingValuesForUserAsync(long userId)
            {
                return GetAllSettingValuesForUserAsync(new UserIdentifier(AbpSession.TenantId, userId));
            }
    
            public async Task<IReadOnlyList<ISettingValue>> GetAllSettingValuesForUserAsync(UserIdentifier user)
            {
                return (await GetReadOnlyUserSettings(user)).Values
                    .Select(setting => new SettingValueObject(setting.Name, setting.Value))
                    .ToImmutableList();
            }
    
            /// <summary>
            /// 修改应用级别的设置数据
            /// </summary>
            /// <param name="name"></param>
            /// <param name="value"></param>
            /// <returns></returns>
            [UnitOfWork]
            public virtual async Task ChangeSettingForApplicationAsync(string name, string value)
            {
                await InsertOrUpdateOrDeleteSettingValueAsync(name, value, null, null);
                await _applicationSettingCache.RemoveAsync(ApplicationSettingsCacheKey);
            }
    
            /// <summary>
            /// 修改租户级别的设置数据
            /// </summary>
            /// <param name="tenantId"></param>
            /// <param name="name"></param>
            /// <param name="value"></param>
            /// <returns></returns>
            [UnitOfWork]
            public virtual async Task ChangeSettingForTenantAsync(int tenantId, string name, string value)
            {
                await InsertOrUpdateOrDeleteSettingValueAsync(name, value, tenantId, null);
                await _tenantSettingCache.RemoveAsync(tenantId);
            }
    
            
            [UnitOfWork]
            public virtual Task ChangeSettingForUserAsync(long userId, string name, string value)
            {
                return ChangeSettingForUserAsync(new UserIdentifier(AbpSession.TenantId, userId), name, value);
            }
    
            /// <summary>
            /// 修改用户级别的设置数据
            /// </summary>
            /// <param name="user"></param>
            /// <param name="name"></param>
            /// <param name="value"></param>
            /// <returns></returns>
            public async Task ChangeSettingForUserAsync(UserIdentifier user, string name, string value)
            {
                await InsertOrUpdateOrDeleteSettingValueAsync(name, value, user.TenantId, user.UserId);
                await _userSettingCache.RemoveAsync(user.ToUserIdentifierString());
            }
    
            #endregion
    
            #region Private methods
           
            /// <summary>
            /// 获取值的主要函数
            /// </summary>
            /// <param name="name"></param>
            /// <param name="tenantId"></param>
            /// <param name="userId"></param>
            /// <param name="fallbackToDefault"></param>
            /// <returns></returns>
            private async Task<string> GetSettingValueInternalAsync(string name, int? tenantId = null, long? userId = null, bool fallbackToDefault = true)
            {
                var settingDefinition = _settingDefinitionManager.GetSettingDefinition(name);
    
                //Get for user if defined
                // 获取用户级别的数据
                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;
                    }
                }
    
                //Get for tenant if defined
                //获取租户级别的数据
                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;
                    }
                }
    
                //Get for application if defined
                // 获取应用级别的数据
                if (settingDefinition.Scopes.HasFlag(SettingScopes.Application))
                {
                    var settingValue = await GetSettingValueForApplicationOrNullAsync(name);
                    if (settingValue != null)
                    {
                        return settingValue.Value;
                    }
    
                    if (!fallbackToDefault)
                    {
                        return null;
                    }
                }
    
                //Not defined, get default value
                //获取默认值
                return settingDefinition.DefaultValue;
            }
    
            /// <summary>
            /// 插入/更新/删除设置值
            /// </summary>
            /// <param name="name"></param>
            /// <param name="value"></param>
            /// <param name="tenantId"></param>
            /// <param name="userId"></param>
            /// <returns></returns>
            private async Task<SettingInfo> InsertOrUpdateOrDeleteSettingValueAsync(string name, string value, int? tenantId, long? userId)
            {
                var settingDefinition = _settingDefinitionManager.GetSettingDefinition(name);
                var settingValue = await SettingStore.GetSettingOrNullAsync(tenantId, userId, name);
    
                //Determine defaultValue
                var defaultValue = settingDefinition.DefaultValue;
    
                // 只有定义为继承的才生效
                if (settingDefinition.IsInherited)
                {
                    //For Tenant and User, Application's value overrides Setting Definition's default value.
                    if (tenantId.HasValue || userId.HasValue)
                    {
                        var applicationValue = await GetSettingValueForApplicationOrNullAsync(name);
                        if (applicationValue != null)
                        {
                            defaultValue = applicationValue.Value;
                        }
                    }
    
                    //For User, Tenants's value overrides Application's default value.
                    if (userId.HasValue && tenantId.HasValue)
                    {
                        var tenantValue = await GetSettingValueForTenantOrNullAsync(tenantId.Value, name);
                        if (tenantValue != null)
                        {
                            defaultValue = tenantValue.Value;
                        }
                    }
                }
    
                //No need to store on database if the value is the default value
                if (value == defaultValue)
                {
                    if (settingValue != null)
                    {
                        await SettingStore.DeleteAsync(settingValue);
                    }
    
                    return null;
                }
    
                //If it's not default value and not stored on database, then insert it
                if (settingValue == null)
                {
                    settingValue = new SettingInfo
                    {
                        TenantId = tenantId,
                        UserId = userId,
                        Name = name,
                        Value = value
                    };
    
                    await SettingStore.CreateAsync(settingValue);
                    return settingValue;
                }
    
                //It's same value in database, no need to update
                if (settingValue.Value == value)
                {
                    return settingValue;
                }
    
                //Update the setting on database.
                settingValue.Value = value;
                await SettingStore.UpdateAsync(settingValue);
    
                return settingValue;
            }
    
            private async Task<SettingInfo> GetSettingValueForApplicationOrNullAsync(string name)
            {
                return (await GetApplicationSettingsAsync()).GetOrDefault(name);
            }
    
            private async Task<SettingInfo> GetSettingValueForTenantOrNullAsync(int tenantId, string name)
            {
                return (await GetReadOnlyTenantSettings(tenantId)).GetOrDefault(name);
            }
    
            private async Task<SettingInfo> GetSettingValueForUserOrNullAsync(UserIdentifier user, string name)
            {
                return (await GetReadOnlyUserSettings(user)).GetOrDefault(name);
            }
    
            private async Task<Dictionary<string, SettingInfo>> GetApplicationSettingsAsync()
            {
                return await _applicationSettingCache.GetAsync(ApplicationSettingsCacheKey, async () =>
                {
                    var dictionary = new Dictionary<string, SettingInfo>();
    
                    var settingValues = await SettingStore.GetAllListAsync(null, null);
                    foreach (var settingValue in settingValues)
                    {
                        dictionary[settingValue.Name] = settingValue;
                    }
    
                    return dictionary;
                });
            }
    
            private async Task<ImmutableDictionary<string, SettingInfo>> GetReadOnlyTenantSettings(int tenantId)
            {
                var cachedDictionary = await GetTenantSettingsFromCache(tenantId);
                lock (cachedDictionary)
                {
                    return cachedDictionary.ToImmutableDictionary();
                }
            }
    
            private async Task<ImmutableDictionary<string, SettingInfo>> GetReadOnlyUserSettings(UserIdentifier user)
            {
                var cachedDictionary = await GetUserSettingsFromCache(user);
                lock (cachedDictionary)
                {
                    return cachedDictionary.ToImmutableDictionary();
                }
            }
    
            private async Task<Dictionary<string, SettingInfo>> GetTenantSettingsFromCache(int tenantId)
            {
                return await _tenantSettingCache.GetAsync(
                    tenantId,
                    async () =>
                    {
                        var dictionary = new Dictionary<string, SettingInfo>();
    
                        var settingValues = await SettingStore.GetAllListAsync(tenantId, null);
                        foreach (var settingValue in settingValues)
                        {
                            dictionary[settingValue.Name] = settingValue;
                        }
    
                        return dictionary;
                    });
            }
    
            private async Task<Dictionary<string, SettingInfo>> GetUserSettingsFromCache(UserIdentifier user)
            {
                return await _userSettingCache.GetAsync(
                    user.ToUserIdentifierString(),
                    async () =>
                    {
                        var dictionary = new Dictionary<string, SettingInfo>();
    
                        var settingValues = await SettingStore.GetAllListAsync(user.TenantId, user.UserId);
                        foreach (var settingValue in settingValues)
                        {
                            dictionary[settingValue.Name] = settingValue;
                        }
    
                        return dictionary;
                    });
            }
    
            public Task<string> GetSettingValueForUserAsync(string name, UserIdentifier user)
            {
                Check.NotNull(name, nameof(name));
                Check.NotNull(user, nameof(user));
    
                return GetSettingValueForUserAsync(name, user.TenantId, user.UserId);
            }
    
            #endregion
    
            #region Nested classes
    
            private class SettingValueObject : ISettingValue
            {
                public string Name { get; private set; }
    
                public string Value { get; private set; }
    
                public SettingValueObject(string name, string value)
                {
                    Value = value;
                    Name = name;
                }
            }
    
            #endregion
        }
    
    获取设置值

    获取设置的值主要是方法GetSettingValueInternalAsync,在这个方法内根据设置范围取值

    /// <summary>
            /// 获取值的主要函数
            /// </summary>
            /// <param name="name"></param>
            /// <param name="tenantId"></param>
            /// <param name="userId"></param>
            /// <param name="fallbackToDefault"></param>
            /// <returns></returns>
            private async Task<string> GetSettingValueInternalAsync(string name, int? tenantId = null, long? userId = null, bool fallbackToDefault = true)
            {
                var settingDefinition = _settingDefinitionManager.GetSettingDefinition(name);
    
                //Get for user if defined
                // 获取用户级别的数据
                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;
                    }
                }
    
                //Get for tenant if defined
                //获取租户级别的数据
                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;
                    }
                }
    
                //Get for application if defined
                // 获取应用级别的数据
                if (settingDefinition.Scopes.HasFlag(SettingScopes.Application))
                {
                    var settingValue = await GetSettingValueForApplicationOrNullAsync(name);
                    if (settingValue != null)
                    {
                        return settingValue.Value;
                    }
    
                    if (!fallbackToDefault)
                    {
                        return null;
                    }
                }
    
                //Not defined, get default value
                //获取默认值
                return settingDefinition.DefaultValue;
            }
    
    获取全部设置值

    按范围覆盖设置值

    
            /// <summary>
            /// 获取全部设置值,用户级别覆盖租户级别,租户级别覆盖应用级别,应用级别覆盖默认值
            /// </summary>
            /// <param name="scopes"></param>
            /// <returns></returns>
            public async Task<IReadOnlyList<ISettingValue>> GetAllSettingValuesAsync(SettingScopes scopes)
            {
                var settingDefinitions = new Dictionary<string, SettingDefinition>();
                var settingValues = new Dictionary<string, ISettingValue>();
    
                //Fill all setting with default values.
                foreach (var setting in _settingDefinitionManager.GetAllSettingDefinitions())
                {
                    settingDefinitions[setting.Name] = setting;
                    settingValues[setting.Name] = new SettingValueObject(setting.Name, setting.DefaultValue);
                }
    
                //Overwrite application settings
                if (scopes.HasFlag(SettingScopes.Application))
                {
                    foreach (var settingValue in await GetAllSettingValuesForApplicationAsync())
                    {
                        var setting = settingDefinitions.GetOrDefault(settingValue.Name);
    
                        //TODO: Conditions get complicated, try to simplify it
                        if (setting == null || !setting.Scopes.HasFlag(SettingScopes.Application))
                        {
                            continue;
                        }
    
                        if (!setting.IsInherited &&
                            ((setting.Scopes.HasFlag(SettingScopes.Tenant) && AbpSession.TenantId.HasValue) || (setting.Scopes.HasFlag(SettingScopes.User) && AbpSession.UserId.HasValue)))
                        {
                            continue;
                        }
    
                        settingValues[settingValue.Name] = new SettingValueObject(settingValue.Name, settingValue.Value);
                    }
                }
    
                //Overwrite tenant settings
                if (scopes.HasFlag(SettingScopes.Tenant) && AbpSession.TenantId.HasValue)
                {
                    foreach (var settingValue in await GetAllSettingValuesForTenantAsync(AbpSession.TenantId.Value))
                    {
                        var setting = settingDefinitions.GetOrDefault(settingValue.Name);
    
                        //TODO: Conditions get complicated, try to simplify it
                        if (setting == null || !setting.Scopes.HasFlag(SettingScopes.Tenant))
                        {
                            continue;
                        }
    
                        if (!setting.IsInherited &&
                            (setting.Scopes.HasFlag(SettingScopes.User) && AbpSession.UserId.HasValue))
                        {
                            continue;
                        }
    
                        settingValues[settingValue.Name] = new SettingValueObject(settingValue.Name, settingValue.Value);
                    }
                }
    
                //Overwrite user settings
                if (scopes.HasFlag(SettingScopes.User) && AbpSession.UserId.HasValue)
                {
                    foreach (var settingValue in await GetAllSettingValuesForUserAsync(AbpSession.ToUserIdentifier()))
                    {
                        var setting = settingDefinitions.GetOrDefault(settingValue.Name);
                        if (setting != null && setting.Scopes.HasFlag(SettingScopes.User))
                        {
                            settingValues[settingValue.Name] = new SettingValueObject(settingValue.Name, settingValue.Value);
                        }
                    }
                }
    
                return settingValues.Values.ToImmutableList();
            }
    
    
    更新设置值

    更新设置主要将设置值数据保存到数据库

    1. 如果设置的值等于默认值,则从数据库中删除数据
    2. 如果数据库中没有数据,则插入
    3. 如果数据库中有数据并且只不等于默认值,则更新数据库数据
    
            /// <summary>
            /// 插入/更新/删除设置值
            /// </summary>
            /// <param name="name"></param>
            /// <param name="value"></param>
            /// <param name="tenantId"></param>
            /// <param name="userId"></param>
            /// <returns></returns>
            private async Task<SettingInfo> InsertOrUpdateOrDeleteSettingValueAsync(string name, string value, int? tenantId, long? userId)
            {
                var settingDefinition = _settingDefinitionManager.GetSettingDefinition(name);
                var settingValue = await SettingStore.GetSettingOrNullAsync(tenantId, userId, name);
    
                //Determine defaultValue
                var defaultValue = settingDefinition.DefaultValue;
    
                // 只有定义为继承的才生效
                if (settingDefinition.IsInherited)
                {
                    //For Tenant and User, Application's value overrides Setting Definition's default value.
                    if (tenantId.HasValue || userId.HasValue)
                    {
                        var applicationValue = await GetSettingValueForApplicationOrNullAsync(name);
                        if (applicationValue != null)
                        {
                            defaultValue = applicationValue.Value;
                        }
                    }
    
                    //For User, Tenants's value overrides Application's default value.
                    if (userId.HasValue && tenantId.HasValue)
                    {
                        var tenantValue = await GetSettingValueForTenantOrNullAsync(tenantId.Value, name);
                        if (tenantValue != null)
                        {
                            defaultValue = tenantValue.Value;
                        }
                    }
                }
    
                //No need to store on database if the value is the default value
                if (value == defaultValue)
                {
                    if (settingValue != null)
                    {
                        await SettingStore.DeleteAsync(settingValue);
                    }
    
                    return null;
                }
    
                //If it's not default value and not stored on database, then insert it
                if (settingValue == null)
                {
                    settingValue = new SettingInfo
                    {
                        TenantId = tenantId,
                        UserId = userId,
                        Name = name,
                        Value = value
                    };
    
                    await SettingStore.CreateAsync(settingValue);
                    return settingValue;
                }
    
                //It's same value in database, no need to update
                if (settingValue.Value == value)
                {
                    return settingValue;
                }
    
                //Update the setting on database.
                settingValue.Value = value;
                await SettingStore.UpdateAsync(settingValue);
    
                return settingValue;
            }
    

    设计模式

    • 提供者模式: SettingProvider
    • 外观模式:ISettingManagerSettingManager

    总结

    提供者模式和外观模式我认为是ABP中两个最核心的模式,很多功能都是基于这两个模式进行扩展和编码,如:设置功能、菜单功能、特性功能、组织单元功能等等,分析到具体功能我们可以再和设置功能来比较。


    我的公众号

    相关文章

      网友评论

        本文标题:ABP 源码解析 八. 设置管理

        本文链接:https://www.haomeiwen.com/subject/aexvvftx.html