ASP.NET Core 2.2 基礎知識(六) 配置(內含MySql+EF)


先上一段代碼,了解一下 .NET Core 配置數據的結構.

新建一個 控制台項目,添加一個文件 json.json ,文件內容如下:

{
  "country": "cn",
  "person": {
    "id": 1,
    "address": {
      "addName": "chengdu"
    }
  }
}

 

控制台代碼:

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

 

AddJsonFile 方法有多個重載,上面只給出了其中一個,3個參數分別表示:
path:文件的物理路徑;
optional: xml 文檔是這樣寫的:Whether the file is optional. 該文件是否可選.true 表示可選,即如果該文件不存在,不會拋出異常,下面的所有顯示都為空;false 表示不可選,即如果該文件不存在,會拋出異常.
reloadOnChange:如果文件修改了,是否重新加載.

 

配置提供程序

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

  • 命令行配置提供程序
  • 環境變量配置提供程序
  • 文件配置提供程序
  • Key-per-file配置提供程序
  • 內存配置提供程序
  • 自定義配置提供程序

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

 

1.新建一個 WebAPI 項目,新建一個 TestController

    [Route("api/[controller]")]
    [ApiController]
    public class TestController : ControllerBase
    {

        private readonly IConfiguration _config;

        public TestController(IConfiguration configuration)
        {
            _config = configuration;
        }

        public string Get()
        {
            //讀取配置數據中 Key 為 CommandLineKey 的值,如果沒有這個 Key,則返回默認值: defaultValue
       //讀取配置文件的方法后面會單獨講.
return _config.GetValue("CommandLineKey","defaultValue"); } }

2.修改 CreateWebHostBuilder 方法

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {
            var config = new ConfigurationBuilder().AddCommandLine(args).Build();
            var hostBuilder = WebHost.CreateDefaultBuilder(args);
            return hostBuilder.UseConfiguration(config).UseStartup<Startup>();
        }
    }

3.測試:

1)不傳參數

2)傳入參數

 

 

 

傳參的格式有多種:

dotnet run CommandLineKey1=Value1

dotnet run --CommandLineKey2=Value2

dotnet run --CommandLineKey3 Value3

dotnet run /CommandLineKey4=Value4

dotnet run /CommandLineKey5 Value5

此外,傳入的以單橫杠"-"或者雙橫杠"--"作為前綴的 Key 支持替換:

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {        
            Dictionary<string, string> switchMapping = new Dictionary<string, string>
            {
                {"-key", "CommandLineKey"},
                {"--key", "CommandLineKey"},
            };

            IConfigurationRoot config = new ConfigurationBuilder().AddCommandLine(args, switchMapping).Build();
            return WebHost.CreateDefaultBuilder().UseConfiguration(config).UseStartup<Startup>();
        }
    }

測試圖:

兩種情況都會顯示

 實際上,在 2.X 版本,CreateDefaultBuilder(args) 方法內部已經調用了 AddCommandLine(args) 方法,我們不需要再調一次了.源碼(部分)如下:

 

二.環境變量配置提供程序 : AddEnvironmentVariables 

在 2.X 版本,CreateDefaultBuilder(args) 方法內部已經調用了 AddEnvironmentVariables() 方法,我們不需要再調一次了.源碼(部分)如下:

 

其實,紅框框起來的方法都是配置提供程序.

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

新建如下控制器:

    [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {

        private readonly IConfiguration _config;

        public TestController(IConfiguration configuration)
        {
            _config = configuration;
        }

        public IEnumerable<KeyValuePair<string, string>> GetAll()
        {
            return _config.AsEnumerable();
        }

        public string Get(string key)
        {           
            return _config.GetValue(key, "defaultValue");
        }
    }

 

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

 

我們試試查詢紅框標注的環境變量:

 

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

以 AddJsonFile 為例講解,其他兩個都差不多.

從上面的源碼截圖中我們已經看到,在使用 CreateDefaultBuilder 方法初始化新的 WebHostBuilder 時,會自動調用 AddJsonFile 兩次,依次從下面兩個文件加載配置:

appsettings.json : 首先讀取該文件.

appsettings.{Environment}.json : 再讀取此文件,該文件中的配置會替代 appsettings.json 文件中的值.

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

appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "FirstSection": {
    "SecondSection": "hello world" } 
}

appsettings.{Environment}.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "FirstSection": {
    "SecondSection": "fuck u" } 
}

請求結果:

 

下面我們來創建自己的配置文件:

在當前項目下新建一個 jsonconfig.json 文件:

{
  "id": 1,
  "name": "wjire" 
}

CreateWebHostBuilder 方法修改如下:

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

 圖就不上了...

 

附:

AddIniFile 配置文件:

[section0]
key0=value
key1=value
[section1]
subsection:key=value
[section2:subsection0]
key=value
[section2:subsection1]
key=value

 

AddXmlFile 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<section0>
<key0>value</key0>
<key1>value</key1>
</section0>
<section1>
<key0>value</key0>
<key1>value</key1>
</section1>
</configuration>

 

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

這個提供程序有點特別,它是將文件名(含擴展名)作為 Key,文件內容作為 Value.

示例:

在當前項目下新建一個 filename.txt 文件,文件內容就一句話: hello world 

        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {
            IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args);
            return hostBuilder.ConfigureAppConfiguration((context, builder) =>
                {
                    //param1:文件所在目錄的物理路徑
                    //param2:該文件是否可選
                    builder.AddKeyPerFile(context.HostingEnvironment.ContentRootPath, true);
                }).UseStartup<Startup>();
        }

 控制器如下:

    [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {

        private readonly IConfiguration _config;

        public TestController(IConfiguration configuration)
        {
            _config = configuration;
        }

        public string Get(string key)
        {            
            return _config.GetValue(key, "defaultValue");
        }
    }

 

 

五. 內存配置提供程序  AddInMemoryCollection

這個很簡單,直接上圖:

        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {
            Dictionary<string, string> memoryCollection = new Dictionary<string, string>
            {
                {"id","1" },
                {"name","wjire" }
            };
            IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args);
            return hostBuilder.ConfigureAppConfiguration((context, builder) =>
            {
                builder.AddInMemoryCollection(memoryCollection);
            }).UseStartup<Startup>();
        }

 

 

六.自定義配置提供程序

該示例演示了如果使用EF創建從數據庫(MySql)讀取配置的提供程序.

第一步:安裝 MySqlEF

第二步:創建數據庫表:

CREATE TABLE `myconfigs` (
  `Id` varchar(20) NOT NULL DEFAULT '',
  `value` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

第三步:創建實體類

    public class MyConfig
    {
        public string Id { get; set; }

        public string Value { get; set; }
    }

第四步:創建數據庫上下文

    public class MyConfigContext : DbContext
    {
        public MyConfigContext(DbContextOptions<MyConfigContext> options) : base(options)
        {
        }

        public DbSet<MyConfig> MyConfigs { get; set; }
    }

第五步:創建配置數據源

    public class MyConfigSource : IConfigurationSource
    {
        private readonly Action<DbContextOptionsBuilder> _optionsAction;

        public MyConfigSource(Action<DbContextOptionsBuilder> optionsAction)
        {
            _optionsAction = optionsAction;
        }


        public IConfigurationProvider Build(IConfigurationBuilder builder)
        {
            return new MyConfigProvider(_optionsAction);
        }
    }

第六步:創建配置數據源提供器

    public class MyConfigProvider : ConfigurationProvider
    {
        private Action<DbContextOptionsBuilder> OptionsAction { get; }

        public MyConfigProvider(Action<DbContextOptionsBuilder> optionsAction)
        {
            OptionsAction = optionsAction;
        }
        
        //從數據庫讀取配置
        public override void Load()
        {
            DbContextOptionsBuilder<MyConfigContext> builder = new DbContextOptionsBuilder<MyConfigContext>();
            OptionsAction(builder);
            using (MyConfigContext dbContext = new MyConfigContext(builder.Options))
            {
                //Data 是基類 ConfigurationProvider 的屬性,用來存儲配置數據源的.
                Data = !dbContext.MyConfigs.Any()//判斷表是否有數據
                    ? CreateAndSaveDefaultValues(dbContext)//如果沒有數據,則添加一些數據,存儲到配置數據源中.
                    : dbContext.MyConfigs.ToDictionary(c => c.Id, c => c.Value);//如果有數據,讀取出來,存儲到配置數據源中.
            }
        }
        
        private static IDictionary<string, string> CreateAndSaveDefaultValues(MyConfigContext dbContext)
        {
            Dictionary<string, string> configValues = new Dictionary<string, string>
            {
                { "1", "refuge" },
                { "2", "36" },
                { "3", "chengdu" }
            };
            dbContext.MyConfigs.AddRange(configValues.Select(kvp => new MyConfig
            {
                Id = kvp.Key,
                Value = kvp.Value
            }).ToArray());
            dbContext.SaveChanges();
            return configValues;
        }
    }

第七步:創建擴展方法,對外公開自定義的數據提供程序

    public static class MyConfigExtension
    {
        public static IConfigurationBuilder AddCustomConfigurationProviderApp(this IConfigurationBuilder builder, Action<DbContextOptionsBuilder> optionsAction)
        {
            return builder.Add(new MyConfigSource(optionsAction));
        }
    }

第八步:創建測試用的控制器

    [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {

        private readonly IConfiguration _config;

        public TestController(IConfiguration configuration)
        {
            _config = configuration;
        }
        
        //查詢所有
        public IEnumerable<KeyValuePair<string, string>> GetAll()
        {
            return _config.AsEnumerable();
        }

        //查詢某個key
        public string Get(string key)
        {
            return _config.GetValue(key, "defaultValue");
        }
    }

第九步:調用自定義配置提供程序

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {

            IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder(args);
            return hostBuilder.ConfigureAppConfiguration((context, builder) =>
                {
                    builder.AddCustomConfigurationProviderApp(options => options.UseMySql("Server=localhost;Database=test;User=root"));
                }).UseStartup<Startup>();
        }
    }

測試結果:

1)查詢所有,可以看到,我們新增的配置數據已經加到系統中了;(看右邊的滾動條就知道總共有很多很多配置...)

2)查詢特定key

 

如果使用該方式提供配置數據,需要注意以下兩點:

1.提供程序在啟動時就將數據庫表讀入配置,不會基於每個key查詢數據庫;

2.應用啟動后,更新數據庫,配置不會更新.

 

上面講了如果提供配置數據,下面講獲取配置的幾種常用方法:

一.GetValue 上面已經演示過了,這里不再重復.只把常用的重載方法列出來

GetValue<T>("key") 如果 key 不存在,則會拋異常;

GetValue<T>("key",T default) 如果 key 不存在,則返回默認值

 

二. T Get<T>()

對於下面這個 json 文件:

 

{
  "name": "refuge",
  "age": 36, 
  "address": {
    "city": "chengdu" 
  } 
}

定義一個實體:

    public class Person
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public Address Address { get; set; }
    }

    public class Address
    {
        public string City { get; set; }
    }

控制器:

    [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {

        private readonly IConfiguration _config;

        public TestController(IConfiguration configuration)
        {
            _config = configuration;
        }
      
public string Get() { var person = _config.Get<Person>(); return JsonConvert.SerializeObject(person); } }

調用結果:

 

但是這種方式,個人持反對態度,因為存儲在系統中的配置數據有很多很多,上述方法相當於讀取所有配置數據來反序列化. 

 

三.GetSection

講該方法之前,有必要再熟悉一下配置在系統中的存儲結構到底是怎么樣的.

對於上面那個 json 文件:name": "refuge",

"age": 36, 
  "address": {
    "city": "chengdu" 
  } 
}

將其讀入配置后,存儲的結構如下:

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

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

{"key":"address","value":null} //通過查詢所有配置,確實有這一行,這意味着想通過 GetValue<string>("address") 反序列化為對象是不可能的...
{"key":"address:city","value":"chengdu"}]

對於上面這些配置,"refuge" 和 "36" 是可以通過第一個方法 GetValue<string>("name") , GetValue<string>("age") 來獲取.

 

 

 而 "chengdu" 只能用 GetValue<string>("address:city") 方法獲取了.

因此,要通過該方法把上述json文件的內容轉成 Person 對象,則需要對 json 文件進行修改,添加一個 "person" 節點:

{
  "person": {
    "name": "refuge",
    "age": 36,
    "address": {
      "city": "chengdu"
    }
  } 
}

Action:

        public string Get()
        {
            //方法一:
            {
                Person person = _config.GetSection("person").Get<Person>();
                return JsonConvert.SerializeObject(person);
            }

            //方法二:
            {
                //Person person = new Person();
                //_config.GetSection("person").Bind(person);
                //return JsonConvert.SerializeObject(person);
            }
        }

方法一明顯要帥氣得多!

問題又來了,如果我只想反序列化 "address" 節點的值,怎么辦呢?下面這個方法可以完成.

 

四.GetChildren

Action:

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

 

 

 

貌似完了.

 

2019年1月6日補充:

上面提到的全是從當前項目加載配置,ASP.NET Core 2.0 提供了從外部程序集加載配置的方法

需要實現該接口:

  public interface IHostingStartup
  {
    void Configure(IWebHostBuilder builder);
  }

示例: 

新建類庫 : ExternalAssembly

namespace ExternalAssembly
{
    public class ExternalConfig : IHostingStartup
    {
        public void Configure(IWebHostBuilder builder)
        {
            Dictionary<string, string> memoryCollection = new Dictionary<string, string>
            {
                {"id","1" },
                {"name","refuge"}
            };
            builder.ConfigureAppConfiguration((context, configBuilder) =>
                {
                    configBuilder.AddInMemoryCollection(memoryCollection);
                });
        }
    }
}

這里要特別注意:

ConfigureAppConfiguration 方法需要安裝nuget包:

 

創建一個 WebAPI 項目,添加對上述類庫的引用,並在 Program 類中添加對該類庫的調用聲明(紅色標注):

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

[assembly: HostingStartup(typeof(ExternalAssembly.ExternalConfig))]

namespace Demo5
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {
            return WebHost.CreateDefaultBuilder(args).UseStartup<Startup>();
        }
    }
}

 

圖就不上了.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM