• Asp .NetCore 从数据库加载配置(一)


            一般来说,Asp .NetCore 应用从官方默认的appsetting.json文件中读取就好,而且利用option模式中的 IOptionsSnapSot、IOptionsMonitor可以动态加载配置,你可以直接修稿json文件来保证。但是这样也有一定的不方便之处:你的配置放在单个json文件,加入你的为服务项目有10个,如果因为项目需求变了,要更改配置,就要到10个json文件中修改,而如果这10个项目的配置都在一个数据库中,那么只需一个脚本就能改好了。

            不过要从数据库动态同步加载需要做一些额外的工作,本节就先介绍静态加载数据库配置。

    目录

    1.搭建基本框架

    a. 创建asp .netcore 项目

     b.创建实体类,数据文上下文类

    c.添加数据库连接信息

     d.迁移、更新数据库

    2.编写自定义配置源

    2.1 实现ConfigurationProvider类

    2.2 ConfigurationSource

     2.3 配置ConfigureAppConfiguration



    1.搭建基本框架

            由于这里不是重点,因此只是简单的介绍关键步骤,如果有不懂的具体可以看此.NET 6.0 - Connect to MySQL Database with Entity Framework Core,有详细讲解。

    a. 创建asp .netcore 项目

            可以创建webApi项目也可以创建MVC项目。由于使用的是MySql和EFcore,因为项目要添加下面三个包:

    Pomelo.EntityFrameworkCore.MySql
    Microsoft.EntityFrameworkCore
    Microsoft.EntityFrameworkCore.Tools

     b.创建实体类,数据文上下文类

            实体类保存我们要的配置信息,参考json文件,我们保存为Key-Value模式:

    1. public class ConfigurationEntity
    2. {
    3. [Key]
    4. public string Key { get; set; }
    5. public string Value { get; set; }
    6. }

    然后创建数据库上下文类:

    1. public class ConfigurationDbContext:DbContext
    2. {
    3. public ConfigurationDbContext(DbContextOptions options):base(options) { }
    4. public DbSet? configurationEntities { get; set; }
    5. }

    c.添加数据库连接信息

            一般情况我们可以将数据库的链接配置放到appsettings.json文件中,不过有时候我们不想让用户修改,可以放到机密文件中,机密文件(secret.json)也是asp框架下默认的读取文件之一,其加载顺序在appsettings.json之后,也就是如果你在两个json中都配置了ConnectionString,根据后来居上的法则,secret.json会生效。具体操作如下:

            在解决方案资源管理器窗口,在你当前项目上右键,弹出菜单中选择“管理用户机密

     选择之后就会弹出secrets.json 编辑页面,和appsettings.json同样配置即可:

     然后就可以将我们的数据库上下文类(DbContex)配置到服务中:

    1. builder.Services.AddDbContext(opts =>
    2. opts.UseMySql(ConnectionString, ServerVersion.AutoDetect(ConnectionString)));

     d.迁移、更新数据库

            编译项目所在的整个解决方案,没有bug后(这点很重要,否则会迁移报错),先迁移数据,

    迁移”(migration)是指根据实体类,生成数据库表的操作,迁移在Code First(模型驱动开发)很常见,区别于早期的先在数据库建表的“数据驱动开发”(Database First).

            EFCore迁移很简单:执行命令:

    Add-Migration InitXXXXX

     执行没问题后,执行更新数据库命令:

    Update-Database

    你就会在你链接的数据库上看到你新建的表,表名为你ConnectionStrings中定义。


    2.编写自定义配置源

            asp.net core官方不支持直接从数据库中读取配置,所以这里我们要自定义配置。而自定义配置都需要实现IConfigurationProvider接口,也就是真正将数据库的字段构建成Dictionary<>。一般我笨都直接集成ConfigurationProvider这个抽象类,ConfigurationProvider中最重要的方法就是Load,自定义配置提供者要实现Load方法来加载配置数据,返回的就是IDictionary,这个字典保存在“Data”属性中,而且还遵循“多层级数据扁平化“规范,如果配置发生了改变,则需要调用ReLoad方法来通知监听配置改变的代码。

    2.1 实现ConfigurationProvider类

    1. public class EFConfigurationProvider:ConfigurationProvider
    2. {
    3. public Action _optionsAction;
    4. public EFConfigurationProvider(Action optionsAction)
    5. {
    6. _optionsAction = optionsAction;
    7. }
    8. public override void Load()
    9. {
    10. var builder = new DbContextOptionsBuilder();
    11. _optionsAction(builder);
    12. using var dbContext = new ConfigurationDbContext(builder.Options);
    13. dbContext.Database.EnsureCreated();
    14. Data=!dbContext.configurationEntities!.Any()
    15. ?CreateAndSaveDefaultValues(dbContext):dbContext.configurationEntities?.ToDictionary(c=>c.Key,c=>c.Value);
    16. }
    17. private static IDictionary<string,string> CreateAndSaveDefaultValues(ConfigurationDbContext context)
    18. {
    19. var configValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
    20. {
    21. { "Pages:HomePage:WelcomeMessage", "Welcome to the ProjectConfigurationDemo Home Page" },
    22. { "Pages:HomePage:ShowWelcomeMessage", "true" },
    23. { "Pages:HomePage:Color", "black" },
    24. { "Pages:HomePage:UseRandomTitleColor", "true" }
    25. };
    26. context.configurationEntities?.AddRange(configValues.Select(kvp => new ConfigurationEntity
    27. {
    28. Key = kvp.Key,
    29. Value = kvp.Value
    30. }).ToArray());
    31. context.SaveChanges();
    32. return configValues;
    33. }
    34. }

    简单说一下,在构造函数中,需要传递一个Action对象,因为我们要在Load函数中链接数据库,所以需要提供连接数据的的信息,当然可以直接加载:

    1. public class EFConfigurationProvider2 : ConfigurationProvider
    2. {
    3. private readonly IConfiguration _configuration;
    4. public EFConfigurationProvider2(IConfiguration configuration)
    5. {
    6. _configuration = configuration;
    7. }
    8. public override void Load()
    9. {
    10. using var dbContext = new ConfigurationDbContext(CreateDbContextOptions());
    11. dbContext.Database.EnsureCreated();
    12. Data = !dbContext.configurationEntities!.Any()
    13. ? CreateAndSaveDefaultValues(dbContext) : dbContext.configurationEntities?.ToDictionary(c => c.Key, c => c.Value);
    14. }
    15. private DbContextOptions CreateDbContextOptions()
    16. {
    17. var connectionString = _configuration.GetConnectionString("MySql");
    18. return new DbContextOptionsBuilder()
    19. .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)).Options;
    20. }
    21. private static IDictionary<string, string> CreateAndSaveDefaultValues(ConfigurationDbContext context)
    22. {
    23. var configValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
    24. {
    25. { "Pages:HomePage:WelcomeMessage", "Welcome to the ProjectConfigurationDemo Home Page" },
    26. { "Pages:HomePage:ShowWelcomeMessage", "true" },
    27. { "Pages:HomePage:Color", "black" },
    28. { "Pages:HomePage:UseRandomTitleColor", "true" }
    29. };
    30. context.configurationEntities?.AddRange(configValues.Select(kvp => new ConfigurationEntity
    31. {
    32. Key = kvp.Key,
    33. Value = kvp.Value
    34. }).ToArray());
    35. context.SaveChanges();
    36. return configValues;
    37. }
    38. }

    本质就是,需要将数据库的链接信息传递到DbContextOptions ,以供

    ConfigurationDbContext使用。

    在Load函数中,我们会判断数据中是否有数据,如果没有,则添加一点默认数据,否则加载新的数据并转化为字典类型。

    2.2 ConfigurationSource

            有了配置提供者,我们就能编写配置源,服务加载不同的配置总是加载不同的配置源而不是配置提供者,IConfigurationSource中要实现Builder方法返回的就是IConfigurationProvider.

    1. public class EFConfigurationSource : IConfigurationSource
    2. {
    3. private readonly Action _optionsAction;
    4. public EFConfigurationSource(Action optionsAction)
    5. {
    6. _optionsAction = optionsAction;
    7. }
    8. public IConfigurationProvider Build(IConfigurationBuilder builder)
    9. {
    10. return new EFConfigurationProvider(_optionsAction);
    11. }
    12. }

     2.3 配置ConfigureAppConfiguration

            一切就绪,我们在ConfigureAppConfiguration配置,也就是将我们编写的配置源加载进去

    1. var builder = WebApplication.CreateBuilder(args);
    2. var ConnectionString = builder.Configuration.GetConnectionString("MySql");
    3. builder.Host.ConfigureAppConfiguration((_, configBuilder) =>
    4. {
    5. var config = configBuilder.Build();
    6. var configSource = new EFConfigurationSource(opts =>
    7. opts.UseMySql(ConnectionString, ServerVersion.AutoDetect(ConnectionString)));
    8. configBuilder.Add(configSource);
    9. });

            至此,在执行:

    var app = builder.Build();

    后,配置中就能使用读取了数据库的配置了,不过在这里,我们要删除下面的代码,因为已经不需要了:

    1. //builder.Services.AddDbContext(opts =>
    2. // opts.UseMySql(ConnectionString, ServerVersion.AutoDetect(ConnectionString)));

    运行后就发现,数据库中多了一些我们在CreateAndSaveDefaultValues函数中提供的默认几组数据:

     为了进一步验证读取,我们又加了两条数据,并根据配置扁平化处理,用“:”隔开,这样等价于appsettings.json中:

    1. "Wang":{
    2. "Age":23,
    3. "Name":"xiao wang"
    4. }

    然后对应的实体类:

    1. public class Student
    2. {
    3. public string? Name { get; set; }
    4. public int Age { get; set; }
    5. }

    最后读取,并打印:

    1. var app = builder.Build();
    2. var wang = app.Services.GetRequiredService>().Value;
    3. Console.WriteLine($"{wang.Name}---{wang.Age}");

    控制台上就有了我们要的数据。


    至此从数据库加载数据的基本操作就完成了,其实从数据库中家在配置,在官方文档中有详细教程:Custom configuration provider,不过这个教程也没有实现动态更新配置,根据前面绿色背景的文字,倒是提供了思路,我在下一节将实现动态更新配置。

  • 相关阅读:
    kubernetes实战入门-Service
    Spring Bean的生命周期源码解析
    入门电机系列之5编码器
    Vue进阶(幺陆玖)信创适配改造
    图文手把手教程--ESP32 MQTT连接腾讯云物联网平台及OTA固件升级
    windows的命令行的一些操作
    轻松驾驭Hive数仓,数据分析从未如此简单!
    想要精通算法和SQL的成长之路 - 加油站
    SpringMvc向request域中设置数据
    点云 数据 (偏向于研究大小)
  • 原文地址:https://blog.csdn.net/q__y__L/article/details/127647591