.NET 配置体系实战:深入 Microsoft.Extensions.Configuration.Ini INI 配置提供程序
2026/9/20 10:18:35 网站建设 项目流程
  • 语言运行时
  • 标准库
  • JIT编译
  • 编译器

【免费下载链接】runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.

项目地址:https://gitcode.com/GitHub_Trending/runtime6/runtime
点击查看免费下载

导读

本文聚焦 dotnet/runtime 仓库中Microsoft.Extensions.Configuration.Ini包,它是 .NET 官方配置体系(Microsoft.Extensions.Configuration)的 INI 文件提供程序实现,用于把 INI 文件(如appsettings.ini)解析为统一的IConfiguration键值配置模型。你将掌握AddIniFile/AddIniStream的完整用法、INI 语法解析规则(注释、节、引号、嵌套节)、错误处理行为以及optional/reloadOnChange等关键参数,并从源码级理解其底层解析实现。

包定位与仓库位置

Microsoft.Extensions.Configuration.Ini是 .NET 官方配置提供程序家族的一员,其实现代码位于仓库 src/libraries/Microsoft.Extensions.Configuration.Ini,主要包含:

  • src/:核心实现(扩展方法、Source、Provider、流解析器)
  • ref/:公共 API 参考(Microsoft.Extensions.Configuration.Ini.cs)
  • tests/:完整的行为测试套件
  • README.md:本包使用文档

从项目文件 Microsoft.Extensions.Configuration.Ini.csproj 可见,它依赖Microsoft.Extensions.ConfigurationMicrosoft.Extensions.Configuration.FileExtensions两个项目,因此天然继承了文件配置提供程序的通用能力(文件监视、可选文件、IFileProvider抽象)。同时它多目标编译支持netstandard2.0与 .NET Framework,兼容面广。

快速上手:从 INI 文件构建配置

根据 README.md 中的示例,读取 INI 配置只需三步:ConfigurationBuilderAddIniFile→ 读取节与键值:

using System; using Microsoft.Extensions.Configuration; class Program { static void Main() { // Build a configuration object from INI file IConfiguration config = new ConfigurationBuilder() .AddIniFile("appsettings.ini") .Build(); // Get a configuration section IConfigurationSection section = config.GetSection("Settings"); // Read configuration values Console.WriteLine($"Server: {section["Server"]}"); Console.WriteLine($"Database: {section["Database"]}"); } }

配套的appsettings.ini内容:

[Settings] Server=example.com Database=Northwind

.csproj中把 INI 文件声明为复制到输出目录的内容项:

<ItemGroup> <Content Include="appsettings.ini"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup>

需要说明:这里的路径appsettings.ini是相对于ConfigurationBuilderBasePath(默认为当前工作目录)解析的。若希望路径相对于程序集所在目录,可在构建前调用SetBasePath(AppContext.BaseDirectory)

扩展方法全览:AddIniFile 与 AddIniStream

核心扩展方法定义在 IniConfigurationExtensions.cs,共 6 个重载(公共 API 见 ref 文件):

方法签名说明
AddIniFile(this IConfigurationBuilder, string path)添加指定路径的 INI 文件,文件必须存在(不可选)、不热重载
AddIniFile(builder, string path, bool optional)控制文件是否可选
AddIniFile(builder, string path, bool optional, bool reloadOnChange)额外控制文件变更时是否自动重载
AddIniFile(builder, IFileProvider? provider, string path, bool optional, bool reloadOnChange)指定自定义IFileProvider访问文件
AddIniFile(builder, Action<IniConfigurationSource>? configureSource)通过委托完全自定义IniConfigurationSource
AddIniStream(builder, Stream stream)直接从一个Stream读取 INI 数据,不经过文件系统

各参数的默认行为从源码可以确认:

  • optional默认false:文件不存在时Build()会抛出FileNotFoundException(测试 IniConfigurationExtensionsTest.cs 中AddIniFile_ThrowsIfFileDoesNotExistAtPath验证了这一点);设为true后文件缺失也不抛异常(AddIniFile_DoesNotThrowsIfFileDoesNotExistAtPathAndOptional)。
  • reloadOnChange默认false:设为true后,FileConfigurationSource会注册文件监视器,文件变化时自动触发配置重载。
  • pathnull或空字符串时抛出ArgumentException,错误消息为资源文件中的Error_InvalidFilePath("File path must be a non-empty string."),见 Strings.resx,测试ThrowExceptionWhenPassingNullAsFilePathThrowExceptionWhenPassingEmptyStringAsFilePath均有覆盖。

INI 语法解析规则(源码级)

INI 文件的解析逻辑集中在 IniStreamConfigurationProvider.cs 的静态方法Read(Stream)中,逐行处理,规则如下:

1. 空白行与注释

空白行直接跳过;以;#/开头的行视为注释被忽略(注意:这三种注释符必须是该行第一个非空白字符)。IniConfigurationProvider.cs 的 XML 文档中给出的完整示例:

[Section:Header] key1=value1 key2 = " value2 " ; comment # comment / comment

测试SupportAndIgnoreComments验证了三种注释符均可被正确忽略。

2. 节(Section)与嵌套节

[开头且以]结尾的行被识别为节头,节名会去除首尾空白后追加ConfigurationPath.KeyDelimiter(即冒号:)作为后续键的前缀:

[Data:Inventory] ConnectionString=AnotherTestConnectionString

等价于键Data:Inventory:ConnectionString。这意味着冒号同时承担"节嵌套分隔符"的职责。测试CanLoadValidIniFromStreamProviderLoadKeyValuePairsFromValidIniFile均使用[Data:Inventory]SubHeader:Provider=MySql验证了这种嵌套路径的展开。

3. 键值对

其余行必须以=分隔键和值,否则抛出FormatException(消息来自Error_UnrecognizedLineFormat:"Unrecognized line format: '{0}'.")。解析时:

  • 键与值的首尾空白会被Trim()去除;
  • 值如果被成对的双引号包裹("value"),双引号会被剥除;
  • 键不区分大小写,因为结果字典使用StringComparer.OrdinalIgnoreCase

测试ShouldRemoveLeadingAndTrailingWhiteSpacesFromKeyAndValue\t key \t = \t value\tkey=value)与ShouldRemoveLeadingAndTrailingWhiteSpacesFromSectionName验证了空白处理;LoadKeyValuePairsFromValidIniFileWithQuotedValues验证了引号剥除。

关于引号有几个精确的边界行为(见测试DoubleQuoteIsPartOfValueIfNotPairedDoubleQuoteIsPartOfValueIfAppearInTheMiddleOfValue):

  • 只有值首字符与末字符都是"时才剥除双引号;
  • 引号不成对(如DefaultConnection="TestConnectionString)时,引号原样保留为值的一部分;
  • 引号出现在值中间(如Test"Connection"String)时同样原样保留。

4. 重复键检测

同一键(含节前缀、忽略大小写)出现两次时抛出FormatException,消息来自Error_KeyIsDuplicated:"A duplicate key '{0}' was found."。测试ThrowExceptionWhenKeyIsDuplicated验证了[Data:DefaultConnection]下的ConnectionString[Data]下以冒号写的DefaultConnection:ConnectionString会被判定为同一键。

5. 无节头文件

不写节头、直接用冒号前缀书写完整键路径也是合法的,测试LoadKeyValuePairsFromValidIniFileWithoutSectionHeader验证了这一点:

DefaultConnection:ConnectionString=TestConnectionString Data:Inventory:Provider=MySql

解析器内部实现:Read 方法剖析

IniStreamConfigurationProvider.Read(Stream)是整个包的解析核心,从源码看其流程如下(IniStreamConfigurationProvider.cs):

  1. StringComparer.OrdinalIgnoreCase创建Dictionary<string, string?>作为数据容器;
  2. StreamReader逐行读取,sectionPrefix变量记录当前节前缀(初始为空字符串);
  3. 每行先Trim():空白行continue;首字符为;/#//的行continue
  4. 若以[开头且以]结尾,则把括号内的内容Trim()后拼接ConfigurationPath.KeyDelimiter更新sectionPrefix
  5. 否则查找=分隔符,找不到就抛FormatException
  6. 拼接key = sectionPrefix + 键部分.Trim(),值部分Trim(),再按规则处理成对双引号;
  7. data.ContainsKey(key)则抛重复键FormatException,否则写入字典。

IniConfigurationProviderIniStreamConfigurationProvider都只是把Data = IniStreamConfigurationProvider.Read(stream)Data = Read(stream)作为Load的实现(见 IniConfigurationProvider.cs),所以文件与流走的是同一套解析逻辑,任何格式行为在两个入口上完全一致。

IniConfigurationSourceIniStreamConfigurationSource分别是文件型与流型配置源的实现(IniConfigurationSource.cs、IniStreamConfigurationSource.cs):前者继承FileConfigurationSource(因此获得IFileProvider、可选文件与热重载能力),后者继承StreamConfigurationSource,其Build方法直接返回对应的 Provider 实例。

从 Stream 加载:AddIniStream 的应用场景

AddIniStream允许绕过文件系统,直接从一个Stream构建配置,非常适合配置内容来自网络、数据库、内存字符串或嵌入式资源的场景:

using var stream = File.OpenRead("config.ini"); var config = new ConfigurationBuilder() .AddIniStream(stream) .Build();

从实现看,AddIniStream 内部注册的是IniStreamConfigurationSource。需要注意的差异:流型配置源不支持热重载,测试ReloadThrowsFromIniStreamProvider明确验证了对流构建的配置调用config.Reload()会抛出InvalidOperationException。此外测试CanLoadValidIniFromStreamProvider还验证了流来源的键同样不区分大小写(config["defaultconnection:ConnectionString"]可命中[DefaultConnection]节)。

错误行为汇总

结合 Strings.resx 与测试,所有异常行为可归纳如下:

场景异常类型消息
path为 null 或空字符串ArgumentException"File path must be a non-empty string."(Error_InvalidFilePath
文件不存在且optional=falseFileNotFoundException"The configuration file '{path}' was not found and is not optional. ..."
行内无=分隔符FormatException"Unrecognized line format: '{0}'."(Error_UnrecognizedLineFormat
节头不完整(如[ConnectionStringFormatException同上(测试ThrowExceptionWhenFoundBrokenSectionHeader
键重复(忽略大小写)FormatException"A duplicate key '{0}' was found."(Error_KeyIsDuplicated
对流配置调用Reload()InvalidOperationException

部署与分发方式

根据 README.md 的 Deployment 章节:Microsoft.Extensions.Configuration.Ini已包含在 ASP.NET Core 共享框架(shared framework)中,因此 ASP.NET Core 应用无需显式安装即可使用;同时该包也作为带外(out-of-band,OOB)包独立发布,任何项目都可以直接通过 NuGet 引用Microsoft.Extensions.Configuration.Ini单独使用。

测试验证与 API 稳定性

本包携带三份测试文件,是理解行为契约的最佳参考:

  • IniConfigurationTest.cs:覆盖节/嵌套节、引号、注释、空白、重复键、坏行格式、缺失文件等全部解析行为;
  • IniConfigurationExtensionsTest.cs:覆盖扩展方法的参数校验与可选文件行为;
  • ConfigurationProviderIniTest.cs:Provider 层面的加载测试。

按照 README 的 Contribution Bar(参见 src/libraries/README.md 中的约定),该包 API 与功能已成熟,但仍会不时扩展新特性,属于"接受新特性、新 API、缺陷修复与性能改进"的组件。

结语

Microsoft.Extensions.Configuration.Ini用极少的代码(核心解析器仅一个Read方法)实现了对 INI 文件完整、健壮的解析:支持注释、节、嵌套节、引号剥除、大小写不敏感键与重复键检测,并通过继承FileConfigurationSource无缝获得可选文件与热重载能力。对于需要与 INI 格式兼容的存量配置或自定义配置源,它是一个值得优先选用的官方实现;其源码与测试也为理解 .NET 配置提供程序架构提供了一个简洁、完整的范本。

  • 语言运行时
  • 标准库
  • JIT编译
  • 编译器

【免费下载链接】runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.

项目地址:https://gitcode.com/GitHub_Trending/runtime6/runtime
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询