• 造轮子之设置管理


    前面我们基础设施基本搭建完毕,后面可以做一些稍微复杂点的功能了,接下来就来实现一个设置管理。
    设置管理一般用做一些系统设置之类的,如邮箱配置等,面向使用人员。而不需要修改我们的配置文件,修改配置文件的方式就偏向于技术人员了。
    话不多说,开造。

    设计结构#

    设置管理中需要2个表,一个是设置组表,比如什么邮箱设置是一个分组,公众号设置是一个分组。一个是设置的值的存储表,用作存储分组的设置。

    using Wheel.Domain.Common;
    
    namespace Wheel.Domain.Settings
    {
        public class SettingGroup : Entity
        {
            public string Name { get; set; }
            public string NormalizedName { get; set; }
    
            public virtual ICollection SettingValues { get; set; }
        }
    }
    
    using Wheel.Domain.Common;
    using Wheel.Enums;
    
    namespace Wheel.Domain.Settings
    {
        public class SettingValue : Entity
        {
            public virtual long SettingGroupId { get; set; }
            public virtual SettingGroup SettingGroup { get; set; }
            public string Key { get; set; }
            public string Value { get; set; }
            public SettingValueType ValueType { get; set; }
            public SettingScope SettingScope { get; set; }
            public string? SettingScopeKey { get; set; }
        }
    }
    

    这里有两个枚举值,分别是SettingValueType和SettingScope
    SettingValueType是Value的类型,如字符串,布尔值,整型,浮点数,主要用于配合前端做页面展示格式以及修改配置时的数据校验。
    SettingScope表示设置的生效范围,比如全局设置,用户设置等等,SettingScopeKey则用作存储范围关联的键值,比如用户范围的话,SettingScopeKey就约定存UserId作为键值,当然也可以自己约定别的唯一数用作关联。后续都可以扩展。

    namespace Wheel.Enums
    {
        public enum SettingValueType
        {
            /// 
            /// 布尔值
            /// 
            Bool,
            /// 
            /// 整型
            /// 
            Int,
            /// 
            /// 长整型
            /// 
            Long,
            /// 
            /// 64位双精度浮点型
            /// 
            Double,
            /// 
            /// 128位精确的十进制值
            /// 
            Decimal,
            /// 
            /// 字符串
            /// 
            String,
            /// 
            /// Json对象
            /// 
            JsonObject
        }
    }
    
    namespace Wheel.Enums
    {
        public enum SettingScope
        {
            /// 
            /// 全局设置
            /// 
            Global,
            /// 
            /// 用户设置
            /// 
            User,
        }
    }
    

    修改DbContext#

    在DbContext中添加代码

    #region Setting
    public DbSet<SettingGroup> SettingGroups { get; set; }
    public DbSet<SettingValue> SettingValues { get; set; }
    #endregion
    
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    
        ConfigureIdentity(builder);
        ConfigureLocalization(builder);
        ConfigurePermissionGrants(builder);
        ConfigureMenus(builder);
        ConfigureSettings(builder);
    }
    
    void ConfigureSettings(ModelBuilder builder)
    {
        builder.Entity(b =>
        {
            b.HasKey(o => o.Id);
            b.Property(o => o.Name).HasMaxLength(128);
            b.Property(o => o.NormalizedName).HasMaxLength(128);
            b.HasMany(o => o.SettingValues).WithOne(o => o.SettingGroup);
            b.HasIndex(o => o.Name);
        });
        builder.Entity(b =>
        {
            b.HasKey(o => o.Id);
            b.Property(o => o.Key).HasMaxLength(128);
            b.Property(o => o.SettingScopeKey).HasMaxLength(128);
            b.Property(o => o.ValueType).HasMaxLength(2048);
            b.HasOne(o => o.SettingGroup).WithMany(o => o.SettingValues);
            b.HasIndex(o => o.Key);
        });
    }
    

    然后执行数据库迁移命令修改数据库即可。

    SettingManager#

    接下来实现一个SettingManager用于管理设置。

    using HotChocolate.Types.Relay;
    using System;
    using System.Linq;
    using Wheel.DependencyInjection;
    using Wheel.Enums;
    using Wheel.EventBus.Distributed;
    using Wheel.EventBus.EventDatas;
    using Wheel.Uow;
    using Wheel.Utilities;
    
    namespace Wheel.Domain.Settings
    {
        public class SettingManager : ITransientDependency
        {
            private readonly IBasicRepositorylong> _settingGroupRepository;
            private readonly IBasicRepositorylong> _settingValueRepository;
    
            private readonly IUnitOfWork _unitOfWork;
            private readonly SnowflakeIdGenerator _snowflakeIdGenerator;
            private readonly IDistributedEventBus _distributedEventBus;
    
            public SettingManager(IBasicRepositorylong> settingGroupRepository, IBasicRepositorylong> settingValueRepository, IUnitOfWork unitOfWork, SnowflakeIdGenerator snowflakeIdGenerator, IDistributedEventBus distributedEventBus)
            {
                _settingGroupRepository = settingGroupRepository;
                _settingValueRepository = settingValueRepository;
                _unitOfWork = unitOfWork;
                _snowflakeIdGenerator = snowflakeIdGenerator;
                _distributedEventBus = distributedEventBus;
            }
    
            public async Task<T?> GetSettingValue<T>(string settingGroupName, string settingKey, SettingScope settingScope = SettingScope.Golbal, string? settingScopeKey = null, CancellationToken cancellationToken = default)
            {
                var settingGroup = await _settingGroupRepository.FindAsync(a => a.Name == settingGroupName, cancellationToken);
    
                if (settingGroup is null)
                {
                    throw new ArgumentException($"SettingGroup: {settingGroup} Not Exist.");
                }
    
                var settingValue = settingGroup?.SettingValues.FirstOrDefault(a => a.Key == settingKey && a.SettingScope == settingScope && a.SettingScopeKey == settingScopeKey);
    
                if (settingValue is null)
                    return default;
    
                if(settingValue.ValueType == SettingValueType.JsonObject)
                    return settingValue.Value.ToObject();
    
                return (T)Convert.ChangeType(settingValue, typeof(T));
            }
    
            public async Task GetSettingValue(string settingGroupName, string settingKey, SettingScope settingScope = SettingScope.Golbal, string? settingScopeKey = null, CancellationToken cancellationToken = default)
            {
                var settingGroup = await _settingGroupRepository.FindAsync(a => a.Name == settingGroupName, cancellationToken);
    
                if (settingGroup is null)
                {
                    throw new ArgumentException($"SettingGroup: {settingGroup} Not Exist.");
                }
    
                var settingValue = settingGroup?.SettingValues.FirstOrDefault(a => a.Key == settingKey && a.SettingScope == settingScope && a.SettingScopeKey == settingScopeKey);
                
                return settingValue;
            }
    
            public async Task?> GetSettingValues(string settingGroupName, SettingScope settingScope = SettingScope.Golbal, string? settingScopeKey = null, CancellationToken cancellationToken = default)
            {
                var settingGroup = await _settingGroupRepository.FindAsync(a => a.Name == settingGroupName, cancellationToken);
    
                if (settingGroup is null)
                {
                    throw new ArgumentException($"SettingGroup: {settingGroup} Not Exist.");
                }
    
                var settingValues = settingGroup?.SettingValues.Where(a => a.SettingScope == settingScope && a.SettingScopeKey == settingScopeKey).ToList();
    
                return settingValues;
            }
    
            public async Task SetSettingValue(string settingGroupName, SettingValue settingValue, CancellationToken cancellationToken = default)
            {
                using (var uow = await _unitOfWork.BeginTransactionAsync(cancellationToken))
                {
                    try
                    {
                        var settingGroup = await _settingGroupRepository.FindAsync(a => a.Name == settingGroupName, cancellationToken);
                        if (settingGroup is null)
                            settingGroup = await _settingGroupRepository.InsertAsync(new SettingGroup { Id = _snowflakeIdGenerator.Create(), Name = settingGroupName, NormalizedName = settingGroupName.ToUpper() }, cancellationToken: cancellationToken);
                        
    
                        CheckSettingValueType(settingValue.Value, settingValue.ValueType);
    
                        var sv = await _settingValueRepository.FindAsync(a=> a.SettingGroupId == settingGroup.Id && a.Id == settingValue.Id, cancellationToken);
                        if(sv is null)
                        {
                            settingValue.Id = _snowflakeIdGenerator.Create();
                            settingValue.SettingGroupId = settingGroup.Id;
                            await _settingValueRepository.InsertAsync(settingValue, cancellationToken: cancellationToken);
                        }
                        else
                            await _settingValueRepository.UpdateAsync(settingValue, cancellationToken: cancellationToken);
                        
                        await uow.CommitAsync(cancellationToken);
                        await _distributedEventBus.PublishAsync(new UpdateSettingEventData() { GroupName = settingGroupName, SettingScope = settingValue.SettingScope, SettingScopeKey = settingValue.SettingScopeKey });
                    }
                    catch(Exception ex)
                    {
                        await uow.RollbackAsync(cancellationToken);
                        ex.ReThrow();
                    }
                }
            }
            public async Task SetSettingValues(string settingGroupName, List settingValues, CancellationToken cancellationToken = default)
            {
                using (var uow = await _unitOfWork.BeginTransactionAsync(cancellationToken))
                {
                    try
                    {
                        var settingGroup = await _settingGroupRepository.FindAsync(a => a.Name == settingGroupName, cancellationToken);
                        if (settingGroup is null)
                            settingGroup = await _settingGroupRepository.InsertAsync(new SettingGroup { Id = _snowflakeIdGenerator.Create(), Name = settingGroupName, NormalizedName = settingGroupName.ToUpper() }, true, cancellationToken: cancellationToken);
    
                        foreach (var settingValue in settingValues)
                        {
                            CheckSettingValueType(settingValue.Value, settingValue.ValueType);
    
                            var sv = await _settingValueRepository.FindAsync(a => a.SettingGroupId == settingGroup.Id && a.Id == settingValue.Id, cancellationToken);
                            if (sv is null)
                            {
                                settingValue.Id = _snowflakeIdGenerator.Create();
                                settingValue.SettingGroupId = settingGroup.Id;
                                await _settingValueRepository.InsertAsync(settingValue, cancellationToken: cancellationToken);
                            }
                            else
                                await _settingValueRepository.UpdateAsync(settingValue, cancellationToken: cancellationToken);
                        }
                        
                        await uow.CommitAsync(cancellationToken);
                        await _distributedEventBus.PublishAsync(new UpdateSettingEventData() { GroupName = settingGroupName, SettingScope = settingValues.First().SettingScope, SettingScopeKey = settingValues.First().SettingScopeKey });
                    }
                    catch (Exception ex)
                    {
                        await uow.RollbackAsync(cancellationToken);
                        ex.ReThrow();
                    }
                }
            }
    
            private void CheckSettingValueType(string settingValue, SettingValueType settingValueType)
            {
                switch (settingValueType)
                {
                    case SettingValueType.String:
                    case SettingValueType.JsonObject:
                        return;
                    case SettingValueType.Bool:
                        if(bool.TryParse(settingValue, out var _))
                        {
                            return;
                        }
                        else
                        {
                            throw new ArgumentException($"SettingValue: {settingValue} Can Not Parse To Bool Type");
                        }
                    case SettingValueType.Int:
                        if (int.TryParse(settingValue, out var _))
                        {
                            return;
                        }
                        else
                        {
                            throw new ArgumentException($"SettingValue: {settingValue} Can Not Parse To Int Type");
                        }
                    case SettingValueType.Long:
                        if (long.TryParse(settingValue, out var _))
                        {
                            return;
                        }
                        else
                        {
                            throw new ArgumentException($"SettingValue: {settingValue} Can Not Parse To Long Type");
                        }
                    case SettingValueType.Double:
                        if (double.TryParse(settingValue, out var _))
                        {
                            return;
                        }
                        else
                        {
                            throw new ArgumentException($"SettingValue: {settingValue} Can Not Parse To Double Type");
                        }
                    case SettingValueType.Decimal:
                        if (decimal.TryParse(settingValue, out var _))
                        {
                            return;
                        }
                        else
                        {
                            throw new ArgumentException($"SettingValue: {settingValue} Can Not Parse To Decimal Type");
                        }
                }
            }
        }
    }
    
    

    这里CheckSettingValueType就是根据SettingValueType做数据校验,如果不符合条件的则拒绝修改。
    就这样,数据库的设置管理操作基本完成。

    SettingDefinition#

    数据库完成之后,接下来就是业务层面的事情了,这里我们定义一个ISettingDefinition接口,用作设置组结构的基本定义和作用范围,比如我们邮箱设置里面包含什么参数值,类型,默认值是什么。
    ISettingDefinition:

    using Wheel.DependencyInjection;
    using Wheel.Enums;
    
    namespace Wheel.Settings
    {
        public interface ISettingDefinition : ITransientDependency
        {
            string GroupName { get; }
            SettingScope SettingScope { get; }
    
            ValueTaskstring, SettingValueParams>> Define();
        }
    }
    

    EmailSettingDefinition:

    using Wheel.Enums;
    
    namespace Wheel.Settings.Email
    {
        public class EmailSettingDefinition : ISettingDefinition
        {
            public string GroupName => "EmailSetting";
    
            public SettingScope SettingScope => SettingScope.Golbal;
    
            public ValueTaskstring, SettingValueParams>> Define()
            {
                return ValueTask.FromResult(new Dictionary<string, SettingValueParams>
                {
                    { "SenderName", new(SettingValueType.String, "Wheel") },
                    { "Host", new(SettingValueType.String, "smtp.exmail.qq.com") },
                    { "Prot", new(SettingValueType.Int, "465") },
                    { "UserName", new(SettingValueType.String) },
                    { "Password", new(SettingValueType.String) },
                    { "UseSsl", new(SettingValueType.Bool, "true") },
                });
            }
        }
    }
    
    public record SettingValueParams(SettingValueType SettingValueType, string? DefalutValue = null, string? SettingScopeKey = null);
    

    可以看到这里邮件的设置定义:
    GroupName指定是EmailSetting这个分组。
    SettingScope指定了是全局范围的设置。
    SettingValueParams是一个record结构,包含设置值的类型,默认值以及范围的Key值。
    Define里面是一个字典结构,里面定义的邮件发送设置里面所需要的所有参数以及默认值。

    SettingDefinition的作用更多在于当数据库没有存储数据时,作为一个默认的结构以及默认值取用。

    SettingManageAppService#

    接下来就需要提供API给客户端交互了,两个接口即可满足,一个用于获取设置,一个用于修改设置。
    ISettingManageAppService:

    using Wheel.Core.Dto;
    using Wheel.DependencyInjection;
    using Wheel.Enums;
    using Wheel.Services.SettingManage.Dtos;
    
    namespace Wheel.Services.SettingManage
    {
        public interface ISettingManageAppService : ITransientDependency
        {
            Task>> GetAllSettingGroup(SettingScope settingScope = SettingScope.Golbal);
            Task UpdateSettings(SettingGroupDto settingGroupDto, SettingScope settingScope = SettingScope.Golbal);
        }
    }
    

    SettingManageAppService:

    using Wheel.Core.Dto;
    using Wheel.Domain.Settings;
    using Wheel.Domain;
    using Wheel.Enums;
    using Wheel.Services.SettingManage.Dtos;
    using Wheel.Settings;
    
    namespace Wheel.Services.SettingManage
    {
        public class SettingManageAppService : WheelServiceBase, ISettingManageAppService
        {
            private readonly IBasicRepositorylong> _settingGroupRepository;
            private readonly IBasicRepositorylong> _settingValueRepository;
            private readonly SettingManager _settingManager;
    
            public SettingManageAppService(IBasicRepositorylong> settingGroupRepository, IBasicRepositorylong> settingValueRepository, SettingManager settingManager)
            {
                _settingGroupRepository = settingGroupRepository;
                _settingValueRepository = settingValueRepository;
                _settingManager = settingManager;
            }
    
            public async Task>> GetAllSettingGroup(SettingScope settingScope = SettingScope.Golbal)
            {
                var settingDefinitions = ServiceProvider.GetServices().Where(a => a.SettingScope == settingScope);
                var settingGroups = await _settingGroupRepository.GetListAsync(a => a.SettingValues.Any(a => a.SettingScope == settingScope && (settingScope == SettingScope.User ? a.SettingScopeKey == CurrentUser.Id : a.SettingScopeKey == null)));
                foreach (var settingDefinition in settingDefinitions)
                {
                    if (settingGroups.Any(a => a.Name == settingDefinition.GroupName))
                        continue;
                    else
                    {
                        var group = new SettingGroup
                        {
                            Name = settingDefinition.GroupName,
                            NormalizedName = settingDefinition.GroupName.ToUpper(),
                            SettingValues = new List()
                        };
                        foreach (var settings in await settingDefinition.Define())
                        {
                            group.SettingValues.Add(new SettingValue 
                            {
                                Key = settings.Key,
                                Value = settings.Value.DefalutValue,
                                ValueType = settings.Value.SettingValueType,
                                SettingScopeKey = settings.Value.SettingScopeKey,
                                SettingScope = settingScope
                            });
                        }
                        settingGroups.Add(group);
                    }
                }
                var settingGroupDtos = Mapper.Map>(settingGroups);
                return new R>(settingGroupDtos);
            }
    
            public async Task UpdateSettings(SettingGroupDto settingGroupDto, SettingScope settingScope = SettingScope.Golbal)
            {
                var settings = Mapper.Map>(settingGroupDto.SettingValues);
                settings.ForEach(a =>
                {
                    a.SettingScope = settingScope;
                    a.SettingScopeKey = settingScope == SettingScope.User ? CurrentUser.Id : null;
                });
                await _settingManager.SetSettingValues(settingGroupDto.Name, settings);
                return new R();
            }
        }
    }
    

    这里可以看到GetAllSettingGroup的实现,当数据库取值没有改设置组数据时,获取SettingDefinition的结构返回给客户端。

    SettingManageController#

    SettingManageController很简单,就是包装ISettingManageAppService暴露API出去即可。

    using Microsoft.AspNetCore.Mvc;
    using Wheel.Core.Dto;
    using Wheel.Enums;
    using Wheel.Services.SettingManage;
    using Wheel.Services.SettingManage.Dtos;
    
    namespace Wheel.Controllers
    {
        /// 
        /// 设置管理
        /// 
        [Route("api/[controller]")]
        [ApiController]
        public class SettingManageController : WheelControllerBase
        {
            private readonly ISettingManageAppService _settingManageAppService;
    
            public SettingManageController(ISettingManageAppService settingManageAppService)
            {
                _settingManageAppService = settingManageAppService;
            }
    
            /// 
            /// 获取所有设置
            /// 
            /// 设置范围
            /// 
            [HttpGet()]
            public Task>> GetAllSettingGroup(SettingScope settingScope = SettingScope.Golbal)
            {
                return _settingManageAppService.GetAllSettingGroup(settingScope);
            }
            /// 
            /// 更新设置
            /// 
            /// 设置组数据
            /// 设置范围
            /// 
            [HttpPut("{settingScope}")]
            public Task UpdateSettings(SettingGroupDto settingGroupDto, SettingScope settingScope)
            {
                return _settingManageAppService.UpdateSettings(settingGroupDto, settingScope);
            }
        }
    }
    
    

    就这样与客户端的交互API完成了。

    SettingProvider#

    接下来则是需要实现一个给内部业务获取设置的工具。
    SettingProvider用作程序内获取对应设置。直接封装获取全局设置或用户设置。

    using Wheel.DependencyInjection;
    
    namespace Wheel.Settings
    {
        public interface ISettingProvider : ITransientDependency
        {
            public Taskstring, string>> GetGolbalSettings(string groupKey, CancellationToken cancellationToken = default);
            public Task<string> GetGolbalSetting(string groupKey, string settingKey, CancellationToken cancellationToken = default);
            public Task<T> GetGolbalSetting<T>(string groupKey, string settingKey, CancellationToken cancellationToken = default) where T : struct;
    
            public Taskstring, string>> GetUserSettings(string groupKey, CancellationToken cancellationToken = default);
            public Task<string> GetUserSetting(string groupKey, string settingKey, CancellationToken cancellationToken = default);
            public Task<T> GetUserSetting<T>(string groupKey, string settingKey, CancellationToken cancellationToken = default) where T : struct;
        }
    }
    
    using Microsoft.Extensions.Caching.Distributed;
    using Wheel.Core.Users;
    using Wheel.Domain.Settings;
    using Wheel.Enums;
    
    namespace Wheel.Settings
    {
        public class DefaultSettingProvider : ISettingProvider
        {
            private readonly SettingManager _settingManager;
            private readonly IDistributedCache _distributedCache;
            private readonly ICurrentUser _currentUser;
            private readonly IServiceProvider _serviceProvider;
    
            public DefaultSettingProvider(SettingManager settingManager, IDistributedCache distributedCache, ICurrentUser currentUser, IServiceProvider serviceProvider)
            {
                _settingManager = settingManager;
                _distributedCache = distributedCache;
                _currentUser = currentUser;
                _serviceProvider = serviceProvider;
            }
    
            public async Task<string> GetGolbalSetting(string groupKey, string settingKey, CancellationToken cancellationToken = default)
            {
                var settings = await GetGolbalSettings(groupKey, cancellationToken);
                return settings[settingKey];
            }
    
            public async Task<T> GetGolbalSetting<T>(string groupKey, string settingKey, CancellationToken cancellationToken = default) where T : struct
            {
                var settings = await GetGolbalSettings(groupKey, cancellationToken);
                return settings[settingKey].To();
            }
    
            public async Taskstring, string>> GetGolbalSettings(string groupKey, CancellationToken cancellationToken = default)
            {
                var cacheSettings = await GetCacheItem(groupKey, SettingScope.Golbal, cancellationToken: cancellationToken);
                if(cacheSettings is null)
                {
                    var dbSettings = await _settingManager.GetSettingValues(groupKey, SettingScope.Golbal, cancellationToken: cancellationToken);
                    if(dbSettings is null)
                    {
                        var settingDefinition = _serviceProvider.GetServices().FirstOrDefault(a => a.GroupName == groupKey && a.SettingScope == SettingScope.Golbal);
                        if(settingDefinition is null)
                            return new();
                        else
                        {
                            var setting = await settingDefinition.Define();
                            return setting.ToDictionary(a => a.Key, a => a.Value.DefalutValue)!;
                        }
                    }
                    return dbSettings.ToDictionary(a => a.Key, a => a.Value);
                }
                else
                {
                    return cacheSettings.ToDictionary(a => a.Key, a => a.Value);
                }
            }
    
            public async Task<string> GetUserSetting(string groupKey, string settingKey, CancellationToken cancellationToken = default)
            {
                var settings = await GetUserSettings(groupKey, cancellationToken);
                return settings[settingKey];
            }
    
            public async Task<T> GetUserSetting<T>(string groupKey, string settingKey, CancellationToken cancellationToken = default) where T : struct
            {
                var settings = await GetUserSettings(groupKey, cancellationToken);
                return settings[settingKey].To();
            }
    
            public async Taskstring, string>> GetUserSettings(string groupKey, CancellationToken cancellationToken = default)
            {
                var cacheSettings = await GetCacheItem(groupKey, SettingScope.User, settingScopeKey: _currentUser.Id, cancellationToken: cancellationToken);
                if (cacheSettings is null)
                {
                    var dbSettings = await _settingManager.GetSettingValues(groupKey, SettingScope.User, settingScopeKey: _currentUser.Id, cancellationToken: cancellationToken);
                    if (dbSettings is null)
                    {
                        var settingDefinition = _serviceProvider.GetServices().FirstOrDefault(a => a.GroupName == groupKey && a.SettingScope == SettingScope.User);
                        if (settingDefinition is null)
                            return new();
                        else
                        {
                            var setting = await settingDefinition.Define();
                            return setting.ToDictionary(a => a.Key, a => a.Value.DefalutValue)!;
                        }
                    }
                    return dbSettings.ToDictionary(a => a.Key, a => a.Value);
                }
                else
                {
                    return cacheSettings.ToDictionary(a => a.Key, a => a.Value);
                }
            }
            private async Task> GetCacheItem(string groupKey, SettingScope settingScope, string settingScopeKey = null, CancellationToken cancellationToken = default)
            {
                var cacheKey = BuildCacheKey(groupKey, settingScope, settingScopeKey);
                return await _distributedCache.GetAsync>(cacheKey, cancellationToken);
            }
            private string BuildCacheKey(string groupKey, SettingScope settingScope, string settingScopeKey)
            {
                return $"{groupKey}:{settingScope}"+ (settingScope == SettingScope.Golbal ? "" : $":{settingScopeKey}");
            }
        }
    }
    
    
    using Wheel.Domain.Settings;
    using Wheel.Enums;
    
    namespace Wheel.Settings
    {
        public class SettingValueCacheItem
        {
            public string Key { get; set; }
            public string Value { get; set; }
            public SettingValueType ValueType { get; set; }
        }
    }
    

    这里获取设置时优先从缓存读取,若缓存没有,则取数据库,若数据库再没有,则从SettingDefintion中获取默认值。
    那么这里缓存数据从哪里来呢?细心的可以看到上面的SettingManager的修改设置的方法中有一行代码:

    await _distributedEventBus.PublishAsync(new UpdateSettingEventData() { GroupName = settingGroupName, SettingScope = settingValues.First().SettingScope, SettingScopeKey = settingValues.First().SettingScopeKey });
    

    这里通过消息队列,通知更新缓存。

    UpdateSettingEvent#

    using Wheel.Enums;
    
    namespace Wheel.EventBus.EventDatas
    {
        [EventName("UpdateSetting")]
        public class UpdateSettingEventData
        {
            public string GroupName { get; set; }
            public SettingScope SettingScope { get; set; }
            public string? SettingScopeKey { get; set; }
        }
    }
    
    using AutoMapper;
    using Microsoft.Extensions.Caching.Distributed;
    using Wheel.DependencyInjection;
    using Wheel.Domain.Settings;
    using Wheel.EventBus.Distributed;
    using Wheel.EventBus.EventDatas;
    using Wheel.Services.SettingManage.Dtos;
    
    namespace Wheel.EventBus.Handlers
    {
        public class UpdateSettingEventHandler : IDistributedEventHandler<UpdateSettingEventData>, ITransientDependency
        {
    
            private readonly SettingManager _settingManager;
            private readonly IDistributedCache _distributedCache;
            private readonly IMapper _mapper;
    
            public UpdateSettingEventHandler(SettingManager settingManager, IDistributedCache distributedCache, IMapper mapper)
            {
                _settingManager = settingManager;
                _distributedCache = distributedCache;
                _mapper = mapper;
            }
    
            public async Task Handle(UpdateSettingEventData eventData, CancellationToken cancellationToken = default)
            {
                var settings = await _settingManager.GetSettingValues(eventData.GroupName, eventData.SettingScope, eventData.SettingScopeKey, cancellationToken);
                
                await _distributedCache.SetAsync($"Setting:{eventData.GroupName}:{eventData.SettingScope}" + (eventData.SettingScope == Enums.SettingScope.Golbal ? "" : $":{eventData.SettingScopeKey}"), _mapper.Map>(settings));
            }
        }
    }
    

    UpdateSettingEventHandler负责在设置更新之后,获取最新的设置直接塞到缓存当中。
    只需一行代码将SettingProvider加入到WheelServiceBase和WheelControllerBase中,后续就可以很方便的获取设置,不需要频繁在构造器注入:

    public ISettingProvider SettingProvider => LazyGetService();
    

    测试#

    启动程序,测试一下获取设置值,这里可以看到,我们通过SettingProvider成功读取了设置。

    image.png
    就这样,我们完成了我们的设置管理功能。

    轮子仓库地址https://github.com/Wheel-Framework/Wheel
    欢迎进群催更。

    image.png

  • 相关阅读:
    Vue知识点---Vuex、路由
    网康科技 NS-ASG 应用安全网关 SQL注入漏洞复现(CVE-2024-2330)
    基于MATLAB的医学图像配准算法仿真
    lock4j--分布式锁中间件--使用实例
    fdbus之事件循环及线程关系
    【数字通信原理】第三章—信源编码理论
    常用音频接口:TDM,PDM,I2S,PCM
    STM8S TIM1寄存器 PWM呼吸灯
    灰色关联分析(Grey Relational Analysis, GRA)
    kubelet源码分析 创建/更新/调谐 pod
  • 原文地址:https://www.cnblogs.com/fanshaoO/p/17774390.html