视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
.NET Core日志配置的方法
2020-11-27 22:34:59 责编:小采
文档


熟悉ASP.NET的开发者一定对web.config文件不陌生。在ASP.NET环境中,要想添加配置参数,一般也都会在此文件中操作。其中最常用的莫过于AppSettings与ConnectionStrings两项。而要在代码中获得文件中的配置信息,ConfigurationManager则是必不可少需要引入的程序集。

然而到了ASP.NET Core时代,存储与读取配置的方式都发生了改变。

如果对ASP.NET Core项目有所了解的话,应该会看到过appsettings.json这个文件。这里就从JSON文件配置方式开始解释ASP.NET Core中是如何读取配置信息的。

假设有预先设置的appsettings.json文件:

{
 "option1": "value1_from_json",
 "option2": 2,

 "subsection": {
 "suboption1": "subvalue1_from_json"
 },
 "wizards": [
 {
 "Name": "Gandalf",
 "Age": "1000"
 },
 {
 "Name": "Harry",
 "Age": "17"
 }
 ]
}

在代码中读取可以按下面的方式操作:

public class Program
{
 public static IConfiguration Configuration { get; set; }

 public static void Main(string[] args = null)
 {
 var builder = new ConfigurationBuilder()
 .SetBasePath(Directory.GetCurrentDirectory())
 .AddJsonFile("appsettings.json");

 Configuration = builder.Build();

 Console.WriteLine($"option1 = {Configuration["Option1"]}");
 Console.WriteLine($"option2 = {Configuration["option2"]}");
 Console.WriteLine(
 $"suboption1 = {Configuration["subsection:suboption1"]}");
 Console.WriteLine();

 Console.WriteLine("Wizards:");
 Console.Write($"{Configuration["wizards:0:Name"]}, ");
 Console.WriteLine($"age {Configuration["wizards:0:Age"]}");
 Console.Write($"{Configuration["wizards:1:Name"]}, ");
 Console.WriteLine($"age {Configuration["wizards:1:Age"]}");
 Console.WriteLine();

 Console.WriteLine("Press a key...");
 Console.ReadKey();
 }
}

首先,实例化一个ConfigurationBuilder对象,接着设置基础路径。

SetBasePath的操作其实是在ConfigurationBuilder的属性字典里设置FileProvider的值。
public static IConfigurationBuilder SetBasePath(this IConfigurationBuilder builder, string basePath)
{
 ...
 
 return builder.SetFileProvider(new PhysicalFileProvider(basePath));
}

public static IConfigurationBuilder SetFileProvider(this IConfigurationBuilder builder, IFileProvider fileProvider)
{
 ...

 builder.Properties[FileProviderKey] = fileProvider ?? throw new ArgumentNullException(nameof(fileProvider));
 return builder;
}

然后是添加JSON文件。

public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange)
{
 ...

 return builder.AddJsonFile(s =>
 {
 s.FileProvider = provider;
 s.Path = path;
 s.Optional = optional;
 s.ReloadOnChange = reloadOnChange;
 s.ResolveFileProvider();
 });
}

public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, Action<JsonConfigurationSource> configureSource)
 => builder.Add(configureSource);

ConfigurationBuilder里添加了一个JsonConfigurationSource对象。

最后,执行ConfigurationBuilder的Build方法,就可以得到保存配置信息的Configuration对象。

总结例子中的代码,获取配置信息的操作其实就分为两步:

1.生成Configuration对象
2.按键值从Configuration对象中获取信息

生成Configuration对象的步骤至少要有三个基础环节。

1.生成ConfigurationBuilder对象
2.添加ConfigurationSource对象
3.创建Configuration对象

查看创建Configuration对象的代码,会发现内部利用的其实是ConfigurationSource中创建的ConfigurationProvider对象。

public IConfigurationRoot Build()
{
 var providers = new List<IConfigurationProvider>();
 foreach (var source in Sources)
 {
 var provider = source.Build(this);
 providers.Add(provider);
 }
 return new ConfigurationRoot(providers);
}

再看IConfiguratonSource接口,也只有一个Build方法。

public interface IConfigurationSource
{
 IConfigurationProvider Build(IConfigurationBuilder builder);
}

最终创建的Configuration对象,即ConfigurationRoot中包含了所有的ConfigurationProvider,说明配置信息都由这些ConfigurationProvider所提供。

跟踪至ConfigurationRoot类型的构造方法,果然在其生成对象时,对所有ConfigurationProvider进行了加载操作。

public ConfigurationRoot(IList<IConfigurationProvider> providers)
{
 ...

 _providers = providers;
 foreach (var p in providers)
 {
 p.Load();
 ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged());
 }
}

比如JsonConfigurationProvider中:

public override void Load(Stream stream)
{
 try
 {
 Data = JsonConfigurationFileParser.Parse(stream);
 }
 ...
}

通过JSON解析器,将JSON文件的配置信息读取至ConfigurationProvider的Data属性中。这个属性即是用于保存所有配置信息。

 /// <summary>
 /// The configuration key value pairs for this provider.
 /// </summary>
 protected IDictionary<string, string> Data { get; set; }

有了ConfigurationRoot对象后,获取配置信息的操作就很简单了。遍历各个ConfigurationProvider,从中获取第一个匹配键值的数据。

public string this[string key]
{
 get
 {
 foreach (var provider in _providers.Reverse())
 {
 string value;

 if (provider.TryGet(key, out value))
 {
 return value;
 }
 }

 return null;
 }

 ...
}

ConfigurationProvider对象从Data属性获取配置的值。

public virtual bool TryGet(string key, out string value)
=> Data.TryGetValue(key, out value);

在最初的例子中可以看Configuration["wizards:0:Name"]这样的写法,这是因为在Load文件时,存储的方式就是用:为分隔符,以作为嵌套对象的键值。

也可以用另一种方法来写,将配置信息绑定为对象。

先定义对象类型:

public class AppSettings
{
 public string Option1 { get; set; }
 public int Option2 { get; set; }
 public Subsection Subsection { get; set; }
 public IList<Wizards> Wizards { get; set; }
}

public class Subsection
{
 public string Suboption1 { get; set; }
}

public class Wizards
{
 public string Name { get; set; }
 public string Age { get; set; }
}

再绑定对象:

static void Main(string[] args)
{
 var builder = new ConfigurationBuilder()
 .SetBasePath(Directory.GetCurrentDirectory())
 .AddJsonFile("appsettings.json");

 Configuration = builder.Build();

 var appConfig = new AppSettings();
 Configuration.Bind(appConfig);

 Console.WriteLine($"option1 = {appConfig.Option1}");
 Console.WriteLine($"option2 = {appConfig.Option2}");
 Console.WriteLine(
 $"suboption1 = {appConfig.Subsection.Suboption1}");
 Console.WriteLine();

 Console.WriteLine("Wizards:");
 Console.Write($"{appConfig.Wizards[0].Name}, ");
 Console.WriteLine($"age {appConfig.Wizards[0].Age}");
 Console.Write($"{appConfig.Wizards[1].Name}, ");
 Console.WriteLine($"age {appConfig.Wizards[1].Age}");
 Console.WriteLine();

 Console.WriteLine("Press a key...");
 Console.ReadKey();
}

写法变成了常见的对象调用属性方式,但结果是一样的。

除了可以用JSON文件存储配置信息外,ASP.NET Core同时也支持INI与XML文件。当然有其它类型文件时,也可以通过实现IConfigurationSource接口并继承ConfigurationProvider类建立自定义的ConfigrationProvider对象来加载配置文件。

至于文件以外的方式,ASP.NET Core也提供了不少。

  1. 命令行,AddCommandLine
  2. 环境变量,AddEnvironmentVariables
  3. 内存, AddInMemoryCollection
  4. 用户机密,AddUserSecrets
  5. Azure Key Vault,AddAzureKeyVault

选择何种存储与读取配置的方法取决于实际场景,ASP.NET Core已经开放了配置方面的入口,任何接入方式理论上都是可行的。实践方面,则需要开发者们不断去尝试与探索。

下载本文
显示全文
专题