DatabaseCfgSource.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. using Apq.Cfg.Sources;
  2. using Microsoft.Extensions.Configuration;
  3. using Microsoft.Extensions.Configuration.Memory;
  4. using SqlSugar;
  5. namespace Apq.Cfg.Database;
  6. /// <summary>
  7. /// 数据库配置源
  8. /// </summary>
  9. internal sealed class DatabaseCfgSource : IWritableCfgSource
  10. {
  11. private readonly SqlSugarDatabaseProvider _databaseProvider;
  12. private readonly DatabaseOptions _options;
  13. /// <summary>
  14. /// 初始化 DatabaseCfgSource 实例
  15. /// </summary>
  16. /// <param name="options">数据库连接选项</param>
  17. /// <param name="level">配置层级,数值越大优先级越高</param>
  18. /// <param name="isPrimaryWriter">是否为主要写入源</param>
  19. public DatabaseCfgSource(DatabaseOptions options, int level, bool isPrimaryWriter)
  20. {
  21. _options = options;
  22. Level = level;
  23. IsPrimaryWriter = isPrimaryWriter;
  24. _databaseProvider = CreateProvider(options.Provider!);
  25. }
  26. /// <summary>
  27. /// 根据提供程序名称创建 SqlSugar 数据库提供程序
  28. /// </summary>
  29. /// <param name="providerName">数据库提供程序名称</param>
  30. /// <returns>SqlSugar 数据库提供程序实例</returns>
  31. /// <exception cref="ArgumentException">当提供程序名称不受支持时抛出</exception>
  32. private static SqlSugarDatabaseProvider CreateProvider(string providerName)
  33. {
  34. var dbType = providerName.ToLowerInvariant() switch
  35. {
  36. "sqlserver" => DbType.SqlServer,
  37. "mysql" => DbType.MySql,
  38. "postgresql" or "postgres" => DbType.PostgreSQL,
  39. "oracle" => DbType.Oracle,
  40. "sqlite" => DbType.Sqlite,
  41. _ => throw new ArgumentException($"不支持的数据库提供程序: '{providerName}'", nameof(providerName))
  42. };
  43. return new SqlSugarDatabaseProvider(dbType);
  44. }
  45. /// <summary>
  46. /// 获取配置层级,数值越大优先级越高
  47. /// </summary>
  48. public int Level { get; }
  49. /// <summary>
  50. /// 获取是否可写,数据库支持通过 API 写入配置,因此始终为 true
  51. /// </summary>
  52. public bool IsWriteable => true;
  53. /// <summary>
  54. /// 获取是否为主要写入源,用于标识当多个可写源存在时的主要写入目标
  55. /// </summary>
  56. public bool IsPrimaryWriter { get; }
  57. /// <summary>
  58. /// 构建 Microsoft.Extensions.Configuration 的内存配置源,从数据库加载数据
  59. /// </summary>
  60. /// <returns>Microsoft.Extensions.Configuration.Memory.MemoryConfigurationSource 实例</returns>
  61. public IConfigurationSource BuildSource()
  62. {
  63. var data = new List<KeyValuePair<string, string?>>();
  64. try
  65. {
  66. using var cts = new CancellationTokenSource(_options.CommandTimeoutMs);
  67. var configData = _databaseProvider.LoadConfigurationAsync(
  68. _options.ConnectionString!, _options.Table!, _options.KeyColumn!, _options.ValueColumn!,
  69. cts.Token).GetAwaiter().GetResult();
  70. foreach (var (key, value) in configData)
  71. data.Add(new KeyValuePair<string, string?>(key, value));
  72. }
  73. catch { }
  74. return new MemoryConfigurationSource { InitialData = data };
  75. }
  76. /// <summary>
  77. /// 应用配置更改到数据库
  78. /// </summary>
  79. /// <param name="changes">要应用的配置更改</param>
  80. /// <param name="cancellationToken">取消令牌</param>
  81. /// <returns>表示异步操作的任务</returns>
  82. public async Task ApplyChangesAsync(IReadOnlyDictionary<string, string?> changes, CancellationToken cancellationToken)
  83. {
  84. using var timeoutCts = new CancellationTokenSource(_options.CommandTimeoutMs);
  85. using var linked = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
  86. await _databaseProvider.ApplyChangesAsync(
  87. _options.ConnectionString!, _options.Table!, _options.KeyColumn!, _options.ValueColumn!,
  88. changes, linked.Token).ConfigureAwait(false);
  89. }
  90. }