Biwen.Settings 是一個簡易的配置項管理模塊,主要的作用就是可以校驗並持久化配置項,比如將自己的配置存儲到資料庫中,JSON文件中等 使用上也是很簡單,只需要在服務中註入配置, 比如我們有一個GithubSetting的配置項,我們只需要定義好對象然後註入到Service中即可: [De ...
Biwen.Settings 是一個簡易的配置項管理模塊,主要的作用就是可以校驗並持久化配置項,比如將自己的配置存儲到資料庫中,JSON文件中等
使用上也是很簡單,只需要在服務中註入配置,
比如我們有一個GithubSetting的配置項,我們只需要定義好對象然後註入到Service中即可:
[Description("Github配置")]
public class GithubSetting : ValidationSettingBase<GithubSetting>
{
[Description("Github用戶名")]
public string? UserName { get; set; } = "vipwan";
[Description("Github倉庫")]
public string? Repository { get; set; } = "Biwen.Settings";
[Description("Github Token")]
public string? Token { get; set; } = "";
public GithubSetting()
{
//驗證規則
RuleFor(x => x.UserName).NotEmpty().Length(3, 128);
RuleFor(x => x.Repository).NotNull().NotEmpty().Length(3, 128);
RuleFor(x => x.Token).NotNull().NotEmpty().Length(3, 128);
}
}
@inject GithubSetting GithubSetting;//直接對象註入
儘管這樣已經足夠好用且便捷,但是對於習慣了使用IConfiguration
和IOptions
的朋友來說還是有些不習慣,其實實現對IConfiguration的支持還是很簡單的,實現一下IConfigurationProvider
即可,我們來動手實現一個名為BiwenSettingConfigurationProvider
的Provider:
internal class Events
{
/// <summary>
/// Channel隊列
/// </summary>
public static readonly Channel<(bool IsChanged, string? SettingName)> ConfigrationChangedChannel = Channel.CreateUnbounded<(bool IsChanged, string? SettingName)>();
}
internal sealed class BiwenSettingConfigurationSource(bool autoRefresh = true) : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder) => new BiwenSettingConfigurationProvider(autoRefresh);
}
internal class BiwenSettingConfigurationProvider : ConfigurationProvider, IDisposable, IAsyncDisposable
{
public BiwenSettingConfigurationProvider(bool autoRefresh)
{
if (Settings.ServiceRegistration.ServiceProvider is null)
{
throw new BiwenException("必須首先註冊Biwen.Setting模塊,請調用:services.AddBiwenSettings()");
}
if (autoRefresh)
{
StartAlertAsync(cts.Token);
}
}
private CancellationTokenSource cts = new();
/// <summary>
/// 使用Channel通知配置變更,如果有事件更新則重新載入
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task StartAlertAsync(CancellationToken cancellationToken)
{
_ = Task.Run(async () =>
{
while (!cancellationToken.IsCancellationRequested)
{
_ = await Events.ConfigrationChangedChannel.Reader.ReadAsync(cancellationToken);
Load();
//通知配置變更
OnReload();
}
}, cancellationToken);
return Task.CompletedTask;
}
//從SettingManager中載入配置項
public override void Load()
{
Dictionary<string, string?> dics = [];
using var scope = Settings.ServiceRegistration.ServiceProvider.CreateScope();
var settingManager = scope.ServiceProvider.GetRequiredService<ISettingManager>();
var settings = settingManager.GetAllSettings()!;
foreach (var setting in settings)
{
if (setting.SettingContent is null) continue;
if (JsonNode.Parse(setting.SettingContent) is not JsonObject json) continue;
foreach (var item in json)
{
dics.TryAdd($"{setting.SettingName}:{item.Key}", item.Value?.ToString());
}
}
Data = dics;
}
public void Dispose()
{
cts.Cancel();
Events.ConfigrationChangedChannel.Writer.Complete();
}
public ValueTask DisposeAsync()
{
cts.Cancel();
Events.ConfigrationChangedChannel.Writer.Complete();
return ValueTask.CompletedTask;
}
}
內部通過Channel實現變更通知,
internal class ConfigurationMediratorDoneHandler(ILogger<ConfigurationMediratorDoneHandler> logger) : IMediratorDoneHandler
{
public Task OnPublishedAsync<T>(T @event) where T : ISetting, new()
{ Events.ConfigrationChangedChannel.Writer.TryWrite((true, typeof(T).Name));
logger.LogInformation($"Setting Changed: {typeof(T).Name},並通知Configuration刷新!");
return Task.CompletedTask;
}
}
然後老規矩我們擴展一下IServiceCollection
:
public static class ServiceRegistration
{
internal static IServiceCollection AddBiwenSettingConfiguration(this IServiceCollection services)
{
//ConfigurationMediratorDoneHandler
services.AddSingleton<IMediratorDoneHandler, ConfigurationMediratorDoneHandler>();
return services;
}
/// <summary>
/// 提供對IConfiguration,IOptions的支持
/// </summary>
/// <param name="manager"></param>
/// <param name="autoRefresh"></param>
/// <returns></returns>
public static ConfigurationManager AddBiwenSettingConfiguration(
this ConfigurationManager manager, IServiceCollection serviceDescriptors, bool autoRefresh = true)
{
var sp = Settings.ServiceRegistration.ServiceProvider ?? throw new BiwenException("必須首先註冊Biwen.Setting模塊,請調用:services.AddBiwenSettings()");
//添加訂閱
if (autoRefresh)
{
serviceDescriptors.AddBiwenSettingConfiguration();
}
IConfigurationBuilder configBuilder = manager;
configBuilder.Add(new BiwenSettingConfigurationSource(autoRefresh));
var settings = ASS.InAllRequiredAssemblies.ThatInherit(typeof(ISetting)).Where(x => x.IsClass && !x.IsAbstract).ToList();
//註冊ISetting
settings.ForEach(x =>
{
//IOptions DI
manager?.GetSection(x.Name).Bind(GetSetting(x, sp));
});
return manager;
}
static object GetSetting(Type x, IServiceProvider sp)
{
var settingManager = sp.GetRequiredService<ISettingManager>();
var cache = sp.GetRequiredService<IMemoryCache>();
//使用緩存避免重覆反射
var md = cache.GetOrCreate($"GenericMethod_{x.FullName}", entry =>
{
MethodInfo methodLoad = settingManager.GetType().GetMethod(nameof(settingManager.Get))!;
MethodInfo generic = methodLoad.MakeGenericMethod(x);
return generic;
});
return md!.Invoke(settingManager, null)!;
}
}
最後在啟動時調用AddBiwenSettingConfiguration擴展即可
builder.Configuration.AddBiwenSettingConfiguration(builder.Services, true);
最後按下麵的形式註冊就可以了:
@inject GithubSetting GithubSetting;//直接對象註入
@inject IOptions<GithubSetting> IOP; //通過IOptions註入
@inject IConfiguration Configuration;//IConfiguration
...
源代碼我發佈到了GitHub,歡迎star! https://github.com/vipwan/Biwen.Settings
https://github.com/vipwan/Biwen.Settings/tree/master/Biwen.Settings/Extensions/Configuration