先上一段代码,了解一下 .NET Core 配置数据的结构.

新建一个 控制台项目,添加一个文件 json.json ,文件内容如下:

  1. {
  2. "country": "cn",
  3. "person": {
  4. "id": 1,
  5. "address": {
  6. "addName": "chengdu"
  7. }
  8. }
  9. }

 

控制台代码:

  1. private static void Main(string[] args)
  2. {
  3. ConfigurationBuilder builder = new ConfigurationBuilder();
  4. builder.AddJsonFile(path: @"E:\xxx\my\core\VS2017\MyConfig\Demo2\json.json", optional: false, reloadOnChange: true);
  5. IConfigurationRoot config = builder.Build();
  6. Console.WriteLine(config["country"]);//cn
  7. Console.WriteLine(config["person:id"]);//1
  8. Console.WriteLine(config["person:address:addname"]);//chengdu
  9. Console.ReadKey();//修改 json.json 文件中 "id":2
  10. Console.WriteLine(config["person:id"]);//2
  11. Console.ReadKey();
  12. }

 

AddJsonFile 方法有多个重载,上面只给出了其中一个,3个参数分别表示:
path:文件的物理路径;
optional: xml 文档是这样写的:Whether the file is optional. 该文件是否可选.true 表示可选,即如果该文件不存在,不会抛出异常,下面的所有显示都为空;false 表示不可选,即如果该文件不存在,会抛出异常.
reloadOnChange:如果文件修改了,是否重新加载.

 

配置提供程序

ASP.NET Core 常用的共有6种配置提供程序 : 

  • 命令行配置提供程序
  • 环境变量配置提供程序
  • 文件配置提供程序
  • Key-per-file配置提供程序
  • 内存配置提供程序
  • 自定义配置提供程序

一.命令行配置提供程序 : AddCommandLine

 

1.新建一个 WebAPI 项目,新建一个 TestController

  1. [Route("api/[controller]")]
  2. [ApiController]
  3. public class TestController : ControllerBase
  4. {
  5. private readonly IConfiguration _config;
  6. public TestController(IConfiguration configuration)
  7. {
  8. _config = configuration;
  9. }
  10. public string Get()
  11. {
  12. //读取配置数据中 Key 为 CommandLineKey 的值,如果没有这个 Key,则返回默认值: defaultValue
           //读取配置文件的方法后面会单独讲.
  13. return _config.GetValue("CommandLineKey","defaultValue");
  14. }
  15. }

2.修改 CreateWebHostBuilder 方法

  1. public class Program
  2. {
  3. public static void Main(string[] args)
  4. {
  5. CreateWebHostBuilder(args).Build().Run();
  6. }
  7. public static IWebHostBuilder CreateWebHostBuilder(string[] args)
  8. {
  9. var config = new ConfigurationBuilder().AddCommandLine(args).Build();
  10. var hostBuilder = WebHost.CreateDefaultBuilder(args);
  11. return hostBuilder.UseConfiguration(config).UseStartup<Startup>();
  12. }
  13. }

3.测试:

1)不传参数

2)传入参数

 

 

 

传参的格式有多种:

dotnet run CommandLineKey1=Value1

dotnet run –CommandLineKey2=Value2

dotnet run –CommandLineKey3 Value3

dotnet run /CommandLineKey4=Value4

dotnet run /CommandLineKey5 Value5

此外,传入的以单横杠”-“或者双横杠”–“作为前缀的 Key 支持替换:

  1. public class Program
  2. {
  3. public static void Main(string[] args)
  4. {
  5. CreateWebHostBuilder(args).Build().Run();
  6. }
  7. public static IWebHostBuilder CreateWebHostBuilder(string[] args)
  8. {
  9. Dictionary<string, string> switchMapping = new Dictionary<string, string>
  10. {
  11. {"-key", "CommandLineKey"},
  12. {"--key", "CommandLineKey"},
  13. };
  14. IConfigurationRoot config = new ConfigurationBuilder().AddCommandLine(args, switchMapping).Build();
  15. return WebHost.CreateDefaultBuilder().UseConfiguration(config).UseStartup<Startup>();
  16. }
  17. }

测试图:

两种情况都会显示

 实际上,在 2.X 版本,CreateDefaultBuilder(args) 方法内部已经调用了 AddCommandLine(args) 方法,我们不需要再调一次了.源码(部分)如下:

 

二.环境变量配置提供程序 : AddEnvironmentVariables 

在 2.X 版本,CreateDefaultBuilder(args) 方法内部已经调用了 AddEnvironmentVariables() 方法,我们不需要再调一次了.源码(部分)如下:

 

其实,红框框起来的方法都是配置提供程序.

那么,环境变量到底有哪些呢?哪里可以看呢?

新建如下控制器:

  1. [Route("api/[controller]/[action]")]
  2. [ApiController]
  3. public class TestController : ControllerBase
  4. {
  5. private readonly IConfiguration _config;
  6. public TestController(IConfiguration configuration)
  7. {
  8. _config = configuration;
  9. }
  10. public IEnumerable<KeyValuePair<string, string>> GetAll()
  11. {
  12. return _config.AsEnumerable();
  13. }
  14. public string Get(string key)
  15. {
  16. return _config.GetValue(key, "defaultValue");
  17. }
  18. }

 

太多了,只截了其中一小部分:

 

我们试试查询红框标注的环境变量:

 

三.文件配置提供程序 : AddJsonFile , AddIniFile , AddXmlFile

以 AddJsonFile 为例讲解,其他两个都差不多.

从上面的源码截图中我们已经看到,在使用 CreateDefaultBuilder 方法初始化新的 WebHostBuilder 时,会自动调用 AddJsonFile 两次,依次从下面两个文件加载配置:

appsettings.json : 首先读取该文件.

appsettings.{Environment}.json : 再读取此文件,该文件中的配置会替代 appsettings.json 文件中的值.

示例: (红色部分是自己加的)

appsettings.json:

  1. {
  2. "Logging": {
  3. "LogLevel": {
  4. "Default": "Warning"
  5. }
  6. },
  7. "AllowedHosts": "*",
  8. "FirstSection": {
  9. "SecondSection": "hello world"
  10. }
  11. }

appsettings.{Environment}.json:

  1. {
  2. "Logging": {
  3. "LogLevel": {
  4. "Default": "Debug",
  5. "System": "Information",
  6. "Microsoft": "Information"
  7. }
  8. },
  9. "FirstSection": {
  10. "SecondSection": "fuck u"
  11. }
  12. }

请求结果:

 

下面我们来创建自己的配置文件:

在当前项目下新建一个 jsonconfig.json 文件:

  1. {
  2. "id": 1,
  3. "name": "wjire"
  4. }

CreateWebHostBuilder 方法修改如下:

  1. public static IWebHostBuilder CreateWebHostBuilder(string[] args)
  2. {
  3. IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args);
  4. return hostBuilder.ConfigureAppConfiguration((context, builder) =>
  5. {
  6. builder.AddJsonFile(Path.Combine(context.HostingEnvironment.ContentRootPath, "jsonconfig.json"),
  7. true, true);
  8. }).UseStartup<Startup>();
  9. }

 图就不上了…

 

附:

AddIniFile 配置文件:

  1. [section0]
  2. key0=value
  3. key1=value
  4. [section1]
  5. subsection:key=value
  6. [section2:subsection0]
  7. key=value
  8. [section2:subsection1]
  9. key=value

 

AddXmlFile 配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <section0>
  4. <key0>value</key0>
  5. <key1>value</key1>
  6. </section0>
  7. <section1>
  8. <key0>value</key0>
  9. <key1>value</key1>
  10. </section1>
  11. </configuration>

 

四.Key-per-file 配置提供程序  AddKeyPerFile

这个提供程序有点特别,它是将文件名(含扩展名)作为 Key,文件内容作为 Value.

示例:

在当前项目下新建一个 filename.txt 文件,文件内容就一句话: hello world 

  1. public static IWebHostBuilder CreateWebHostBuilder(string[] args)
  2. {
  3. IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args);
  4. return hostBuilder.ConfigureAppConfiguration((context, builder) =>
  5. {
  6. //param1:文件所在目录的物理路径
  7. //param2:该文件是否可选
  8. builder.AddKeyPerFile(context.HostingEnvironment.ContentRootPath, true);
  9. }).UseStartup<Startup>();
  10. }

 控制器如下:

  1. [Route("api/[controller]/[action]")]
  2. [ApiController]
  3. public class TestController : ControllerBase
  4. {
  5. private readonly IConfiguration _config;
  6. public TestController(IConfiguration configuration)
  7. {
  8. _config = configuration;
  9. }
  10. public string Get(string key)
  11. {
  12. return _config.GetValue(key, "defaultValue");
  13. }
  14. }

 

 

五. 内存配置提供程序  AddInMemoryCollection

这个很简单,直接上图:

  1. public static IWebHostBuilder CreateWebHostBuilder(string[] args)
  2. {
  3. Dictionary<string, string> memoryCollection = new Dictionary<string, string>
  4. {
  5. {"id","1" },
  6. {"name","wjire" }
  7. };
  8. IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args);
  9. return hostBuilder.ConfigureAppConfiguration((context, builder) =>
  10. {
  11. builder.AddInMemoryCollection(memoryCollection);
  12. }).UseStartup<Startup>();
  13. }

 

 

六.自定义配置提供程序

该示例演示了如果使用EF创建从数据库(MySql)读取配置的提供程序.

第一步:安装 MySqlEF

第二步:创建数据库表:

  1. CREATE TABLE `myconfigs` (
  2. `Id` varchar(20) NOT NULL DEFAULT '',
  3. `value` varchar(20) NOT NULL DEFAULT '',
  4. PRIMARY KEY (`Id`)
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

第三步:创建实体类

  1. public class MyConfig
  2. {
  3. public string Id { get; set; }
  4. public string Value { get; set; }
  5. }

第四步:创建数据库上下文

  1. public class MyConfigContext : DbContext
  2. {
  3. public MyConfigContext(DbContextOptions<MyConfigContext> options) : base(options)
  4. {
  5. }
  6. public DbSet<MyConfig> MyConfigs { get; set; }
  7. }

第五步:创建配置数据源

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

第六步:创建配置数据源提供器

  1. public class MyConfigProvider : ConfigurationProvider
  2. {
  3. private Action<DbContextOptionsBuilder> OptionsAction { get; }
  4. public MyConfigProvider(Action<DbContextOptionsBuilder> optionsAction)
  5. {
  6. OptionsAction = optionsAction;
  7. }
  8. //从数据库读取配置
  9. public override void Load()
  10. {
  11. DbContextOptionsBuilder<MyConfigContext> builder = new DbContextOptionsBuilder<MyConfigContext>();
  12. OptionsAction(builder);
  13. using (MyConfigContext dbContext = new MyConfigContext(builder.Options))
  14. {
  15. //Data 是基类 ConfigurationProvider 的属性,用来存储配置数据源的.
  16. Data = !dbContext.MyConfigs.Any()//判断表是否有数据
  17. ? CreateAndSaveDefaultValues(dbContext)//如果没有数据,则添加一些数据,存储到配置数据源中.
  18. : dbContext.MyConfigs.ToDictionary(c => c.Id, c => c.Value);//如果有数据,读取出来,存储到配置数据源中.
  19. }
  20. }
  21. private static IDictionary<string, string> CreateAndSaveDefaultValues(MyConfigContext dbContext)
  22. {
  23. Dictionary<string, string> configValues = new Dictionary<string, string>
  24. {
  25. { "1", "refuge" },
  26. { "2", "36" },
  27. { "3", "chengdu" }
  28. };
  29. dbContext.MyConfigs.AddRange(configValues.Select(kvp => new MyConfig
  30. {
  31. Id = kvp.Key,
  32. Value = kvp.Value
  33. }).ToArray());
  34. dbContext.SaveChanges();
  35. return configValues;
  36. }
  37. }

第七步:创建扩展方法,对外公开自定义的数据提供程序

  1. public static class MyConfigExtension
  2. {
  3. public static IConfigurationBuilder AddCustomConfigurationProviderApp(this IConfigurationBuilder builder, Action<DbContextOptionsBuilder> optionsAction)
  4. {
  5. return builder.Add(new MyConfigSource(optionsAction));
  6. }
  7. }

第八步:创建测试用的控制器

  1. [Route("api/[controller]/[action]")]
  2. [ApiController]
  3. public class TestController : ControllerBase
  4. {
  5. private readonly IConfiguration _config;
  6. public TestController(IConfiguration configuration)
  7. {
  8. _config = configuration;
  9. }
  10. //查询所有
  11. public IEnumerable<KeyValuePair<string, string>> GetAll()
  12. {
  13. return _config.AsEnumerable();
  14. }
  15. //查询某个key
  16. public string Get(string key)
  17. {
  18. return _config.GetValue(key, "defaultValue");
  19. }
  20. }

第九步:调用自定义配置提供程序

  1. public class Program
  2. {
  3. public static void Main(string[] args)
  4. {
  5. CreateWebHostBuilder(args).Build().Run();
  6. }
  7. public static IWebHostBuilder CreateWebHostBuilder(string[] args)
  8. {
  9. IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args);
  10. return hostBuilder.ConfigureAppConfiguration((context, builder) =>
  11. {
  12. builder.AddCustomConfigurationProviderApp(options => options.UseMySql("Server=localhost;Database=test;User=root"));
  13. }).UseStartup<Startup>();
  14. }
  15. }

测试结果:

1)查询所有,可以看到,我们新增的配置数据已经加到系统中了;(看右边的滚动条就知道总共有很多很多配置…)

2)查询特定key

 

如果使用该方式提提供配置数据,需要注意以下两点:

1.提供程序在启动时就将数据库表读入配置,不会基于每个key查询数据库;

2.应用启动后,更新数据库,配置不会更新.

 

上面讲了如果提供配置,下面讲获取配置的几种常用方法:

一.GetValue 上面已经演示过了,这里不再重复.只把常用的重载方法列出来

GetValue<T>(“key”) 如果 key 不存在,则会抛异常;

GetValue<T>(“key”,T default) 如果 key 不存在,则返回默认值

 

二. T Get<T>()

对于下面这个 json 文件:

  1. {
  2. "name": "refuge",
  3. "age": 36,
  4. "address": {
  5. "city": "chengdu"
  6. }
  7. }

定义一个实体:

  1. public class Person
  2. {
  3. public string Name { get; set; }
  4. public int Age { get; set; }
  5. public Address Address { get; set; }
  6. }
  7. public class Address
  8. {
  9. public string City { get; set; }
  10. }

控制器:

  1. [Route("api/[controller]/[action]")]
  2. [ApiController]
  3. public class TestController : ControllerBase
  4. {
  5. private readonly IConfiguration _config;
  6. public TestController(IConfiguration configuration)
  7. {
  8. _config = configuration;
  9. }
          
    public string Get()
  10. {
  11. var person = _config.Get<Person>();
  12. return JsonConvert.SerializeObject(person);
  13. }
  14. }

调用结果:

但是这种方式,个人持反对态度,因为存储在系统中的配置数据有很多很多,上述方法相当于读取所有配置数据来反序列化. 

 

三.GetSection

讲该方法之前,有必要再熟悉一下配置在系统中的存储结构到底是怎么样的.

对于上面那个 json 文件:name“: “refuge”,

  1. "age": 36,
  2. "address": {
  3. "city": "chengdu"
  4. }
  5. }

将其读入配置后,存储的结构如下:

  1. {"key":"name","value":"refuge"}

    {"key":"age","value":"36"}

    {"key":"address","value":null} //通过查询所有配置,确实有这一行,这意味着想通过 GetValue<string>("address") 反序列化为对象是不可能的...
  1. {"key":"address:city","value":"chengdu"}]

对于上面这些配置,”refuge” 和 “36” 是可以通过第一个方法 GetValue<string>(“name”) , GetValue<string>(“age”) 来获取.

 而 “chengdu” 只能用 GetValue<string>(“address:city”) 方法获取了.

因此,要通过该方法把上述json文件的内容转成 Person 对象,则需要对 json 文件进行修改,添加一个 “person” 节点:

  1. {
  2. "person": {
  3. "name": "refuge",
  4. "age": 36,
  5. "address": {
  6. "city": "chengdu"
  7. }
  8. }
  9. }

Action:

  1. public string Get()
  2. {
  3. //方法一:
  4. {
  5. Person person = _config.GetSection("person").Get<Person>();
  6. return JsonConvert.SerializeObject(person);
  7. }
  8. //方法二:
  9. {
  10. //Person person = new Person();
  11. //_config.GetSection("person").Bind(person);
  12. //return JsonConvert.SerializeObject(person);
  13. }
  14. }

方法一明显要帅气得多!

问题又来了,如果我只想反序列化 “address” 节点的值,怎么办呢?下面这个方法可以完成.

 

四.GetChildren

Action:

  1. public string Get()
  2. {
  3. IConfigurationSection firstChildSection = _config.GetSection("person").GetChildren().First();
  4. Address address = firstChildSection.Get<Address>();
  5. return JsonConvert.SerializeObject(address);
  6. }

 

 

 

貌似完了.

 

posted on 2019-01-05 16:38 热敷哥 阅读() 评论() 编辑 收藏

版权声明:本文为refuge原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/refuge/p/10220727.html