关于Asp.net core配置信息读取的源码分析梳理

简介: 我们都知道asp.net core配置信息的读取离不开IConfigurationSource和IConfigurationProvider这两个类,ConfigurationSource可以提供一个ConfigurationProvider,然后去读取信息。究竟他们之间有着怎样的千丝万缕,我们一起来看看源码。

概述

我们都知道asp.net core配置信息的读取离不开IConfigurationSource和IConfigurationProvider这两个类,ConfigurationSource可以提供一个ConfigurationProvider,然后去读取信息。究竟他们之间有着怎样的千丝万缕,我们一起来看看源码。

首先我们来建立一个.net core控制台项目,来运行以下代码:

 class Program
    {
        static void Main(string[] args)
        {
            ConfigurationBuilder configBuilder = new ConfigurationBuilder();
            configBuilder.SetBasePath(Directory.GetCurrentDirectory())   
                   .AddJsonFile("appsettings.json");
            var configFile = configBuilder.Build();

            Console.ReadKey();
        }
    }

短短几行 代码看起来很简单,就是用来读取appsettings.json文件中的配置信息。然而我们今天想搞清楚其背后运行的原理,要花点时间。

首先、我们根据代码ConfigurationBuilder configBuilder = new ConfigurationBuilder();知道创建了一个configBuilder对象;

其次,configBuilder.SetBasePath(Directory.GetCurrentDirectory()) 该代码的调用我们也能大概见名知义,获取当前的目录;

接下来,重点来了,configBuilder.AddJsonFile("appsettings.json")的实现究竟是怎样的?我们来看下源码的实现:

f12进去后源码如下:

/// <summary>Extension methods for adding <see cref="T:Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider" />.</summary>
  public static class JsonConfigurationExtensions
  {
    /// <summary>Adds the JSON configuration provider at <paramref name="path" /> to <paramref name="builder" />.</summary>
    /// <param name="builder">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" /> to add to.</param>
    /// <param name="path">Path relative to the base path stored in
    /// <see cref="P:Microsoft.Extensions.Configuration.IConfigurationBuilder.Properties" /> of <paramref name="builder" />.</param>
    /// <returns>The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</returns>
    public static IConfigurationBuilder AddJsonFile(
      this IConfigurationBuilder builder,
      string path)
    {
      return builder.AddJsonFile((IFileProvider) null, path, false, false);
    }
}

紧接着f12再看实现的源码,依然在JsonConfigurationExtensions这个扩展类里面:

    public static IConfigurationBuilder AddJsonFile(
      this IConfigurationBuilder builder,
      IFileProvider provider,
      string path,
      bool optional,
      bool reloadOnChange)
    {
      if (builder == null)
        throw new ArgumentNullException(nameof (builder));
      if (string.IsNullOrEmpty(path))
        throw new ArgumentException(SR.Error_InvalidFilePath, nameof (path));
      return builder.AddJsonFile((Action<JsonConfigurationSource>) (s =>
      {
        s.FileProvider = provider;
        s.Path = path;
        s.Optional = optional;
        s.ReloadOnChange = reloadOnChange;
        s.ResolveFileProvider();
      }));
    }

这时候有没有发现builder.AddJsonFile((Action)这个方法里面出现了一个关键的信息点:JsonConfigurationSource (JsonConfigurationSource 继承抽象类FileConfigurationSource,而FileConfigurationSource:IConfigurationSource) 。 关系如下图:
1.jpg
2.jpg

看完上面这个关系图后,我们紧接着上面builder.AddJsonFile()的实现源码继续f12往下,源码如下:

public static IConfigurationBuilder AddJsonFile(
    this IConfigurationBuilder builder,
    Action<JsonConfigurationSource> configureSource)
  {
    return ConfigurationExtensions.Add<JsonConfigurationSource>(builder, (Action<M0>) configureSource);
  }

我们看到上面的扩展方法实现是ConfigurationExtensions.Add...,再往下看实现:

public static class ConfigurationExtensions
  {
    /// <summary>Adds a new configuration source.</summary>
    /// <param name="builder">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" /> to add to.</param>
    /// <param name="configureSource">Configures the source secrets.</param>
    /// <typeparam name="TSource" />
    /// <returns>The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</returns>
    public static IConfigurationBuilder Add<TSource>(
      this IConfigurationBuilder builder,
      Action<TSource> configureSource)
      where TSource : IConfigurationSource, new()
    {
      TSource source = new TSource();
      if (configureSource != null)
        configureSource(source);
      return builder.Add((IConfigurationSource) source);
    }
}

到这里我们看到了其实就是IConfigurationBuilder调用了Add方法,添加了一个数据源(JsonConfigurationSource),至于JsonConfigurationSource类里面做了什么,我们看下实现。

  public class JsonConfigurationSource : FileConfigurationSource
  {
    /// <summary>Builds the <see cref="T:Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider" /> for this source.</summary>
    /// <param name="builder">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</param>
    /// <returns>A <see cref="T:Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider" /></returns>
    public override IConfigurationProvider Build(IConfigurationBuilder builder)
    {
      this.EnsureDefaults(builder);
      return (IConfigurationProvider) new JsonConfigurationProvider(this);
    }
  }

JsonConfigurationSource类面的Build方法提供了一个JsonConfigurationProvider类,这里再贴下JsonConfigurationProvider类里面的代码:

  /// <summary>A JSON file based <see cref="T:Microsoft.Extensions.Configuration.FileConfigurationProvider" />.</summary>
  public class JsonConfigurationProvider : FileConfigurationProvider
  {
    /// <summary>Initializes a new instance with the specified source.</summary>
    /// <param name="source">The source settings.</param>
    public JsonConfigurationProvider(JsonConfigurationSource source)
      : base((FileConfigurationSource) source)
    {
    }

    /// <summary>Loads the JSON data from a stream.</summary>
    /// <param name="stream">The stream to read.</param>
    public virtual void Load(Stream stream)
    {
      try
      {
        this.set_Data(JsonConfigurationFileParser.Parse(stream));
      }
      catch (JsonException ex)
      {
        throw new FormatException(SR.Error_JSONParseError, (Exception) ex);
      }
    }
  }

关于JsonConfigurationProvider里面的Load就是去读取信息的实现,至于Load的具体实现我们不再深究。我们回到最初的控制台configBuilder.Build(),看看其的实现:

 {
   /// <summary>Returns the sources used to obtain configuration values.</summary>
   public IList<IConfigurationSource> Sources { get; } = (IList<IConfigurationSource>) new List<IConfigurationSource>();

   /// <summary>Gets a key/value collection that can be used to share data between the <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />
   /// and the registered <see cref="T:Microsoft.Extensions.Configuration.IConfigurationProvider" />s.</summary>
   public IDictionary<string, object> Properties { get; } = (IDictionary<string, object>) new Dictionary<string, object>();

   /// <summary>Adds a new configuration source.</summary>
   /// <param name="source">The configuration source to add.</param>
   /// <returns>The same <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</returns>
   public IConfigurationBuilder Add(IConfigurationSource source)
   {
     if (source == null)
       throw new ArgumentNullException(nameof (source));
     this.Sources.Add(source);
     return (IConfigurationBuilder) this;
   }

   /// <summary>Builds an <see cref="T:Microsoft.Extensions.Configuration.IConfiguration" /> with keys and values from the set of providers registered in
   /// <see cref="P:Microsoft.Extensions.Configuration.ConfigurationBuilder.Sources" />.</summary>
   /// <returns>An <see cref="T:Microsoft.Extensions.Configuration.IConfigurationRoot" /> with keys and values from the registered providers.</returns>
   public IConfigurationRoot Build()
   {
     List<IConfigurationProvider> configurationProviderList = new List<IConfigurationProvider>();
     foreach (IConfigurationSource source in (IEnumerable<IConfigurationSource>) this.Sources)
     {
       IConfigurationProvider configurationProvider = source.Build((IConfigurationBuilder) this);
       configurationProviderList.Add(configurationProvider);
     }
     return (IConfigurationRoot) new ConfigurationRoot((IList<IConfigurationProvider>) configurationProviderList);
   }
 }

看到这个源码的时候有没有种豁然开朗的感觉,前面我们说到IConfigurationBuilder调用了Add方法添加一个数据源,并没说添加了一个数据源存在了哪里,到底有什么用处,现在在上面ConfigurationBuilder类里面看到存在了Sources 集合里面。然后configBuilder.Build()

去调用的时候遍历数据源(Sources )集合,紧接着source (IConfigurationSource)调用了Build方法构建了一个configurationProvider对象存到configurationProviderList集合里面,最后在返回一个ConfigurationRoot对象的构造函数里面传递了configurationProviderList集合去执行。

贴上ConfigurationRoot的源码:

  {

    private readonly IList<IConfigurationProvider> _providers;
    private readonly IList<IDisposable> _changeTokenRegistrations;

    /// <summary>Initializes a Configuration root with a list of providers.</summary>
    /// <param name="providers">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationProvider" />s for this configuration.</param>
    public ConfigurationRoot(IList<IConfigurationProvider> providers)
    {
      if (providers == null)
        throw new ArgumentNullException(nameof (providers));
      this._providers = providers;
      this._changeTokenRegistrations = (IList<IDisposable>) new List<IDisposable>(providers.Count);
      foreach (IConfigurationProvider provider in (IEnumerable<IConfigurationProvider>) providers)
      {
        IConfigurationProvider p = provider;
        p.Load();
        this._changeTokenRegistrations.Add(ChangeToken.OnChange((Func<IChangeToken>) (() => p.GetReloadToken()), (Action) (() => this.RaiseChanged())));
      }
    }
}

看到没,最后providers去调用了load方法。

结语

就上面的控制台代码来说IConfigurationSource对应的实现是JsonConfigurationSource;IConfigurationProvider,抽象类ConfigurationProvider对应的实现为JsonConfigurationProvider。如果我们要换成别的文件格式呢?比如ini,怎样自定义配置源呢?大家可以先想想,其实也很简单,下次跟大家分享。

最后说真的,.netCore源码真的特别优秀,很值得花一番时间去看看!从其中可以学到许多架构知识!

相关文章
|
30天前
|
开发框架 .NET 开发者
简化 ASP.NET Core 依赖注入(DI)注册-Scrutor
Scrutor 是一个简化 ASP.NET Core 应用程序中依赖注入(DI)注册过程的开源库,支持自动扫描和注册服务。通过简单的配置,开发者可以轻松地从指定程序集中筛选、注册服务,并设置其生命周期,同时支持服务装饰等高级功能。适用于大型项目,提高代码的可维护性和简洁性。仓库地址:&lt;https://github.com/khellang/Scrutor&gt;
47 5
|
2月前
|
开发框架 .NET C#
在 ASP.NET Core 中创建 gRPC 客户端和服务器
本文介绍了如何使用 gRPC 框架搭建一个简单的“Hello World”示例。首先创建了一个名为 GrpcDemo 的解决方案,其中包含一个 gRPC 服务端项目 GrpcServer 和一个客户端项目 GrpcClient。服务端通过定义 `greeter.proto` 文件中的服务和消息类型,实现了一个简单的问候服务 `GreeterService`。客户端则通过 gRPC 客户端库连接到服务端并调用其 `SayHello` 方法,展示了 gRPC 在 C# 中的基本使用方法。
50 5
在 ASP.NET Core 中创建 gRPC 客户端和服务器
|
1月前
|
开发框架 缓存 .NET
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
31 3
|
14天前
|
开发框架 算法 中间件
ASP.NET Core 中的速率限制中间件
在ASP.NET Core中,速率限制中间件用于控制客户端请求速率,防止服务器过载并提高安全性。通过`AddRateLimiter`注册服务,并配置不同策略如固定窗口、滑动窗口、令牌桶和并发限制。这些策略可在全局、控制器或动作级别应用,支持自定义响应处理。使用中间件`UseRateLimiter`启用限流功能,并可通过属性禁用特定控制器或动作的限流。这有助于有效保护API免受滥用和过载。 欢迎关注我的公众号:Net分享 (239字符)
33 0
|
4月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
53 7
|
4月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
91 0
|
5月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
70 0
|
5月前
|
开发框架 前端开发 安全
ASP.NET MVC 如何使用 Form Authentication?
ASP.NET MVC 如何使用 Form Authentication?
|
5月前
|
开发框架 .NET
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
166 0
|
8月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
223 0