- 语言运行时
- 标准库
- JIT编译
- 编译器
【免费下载链接】runtime
.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
导读
本文聚焦 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.Configuration与Microsoft.Extensions.Configuration.FileExtensions两个项目,因此天然继承了文件配置提供程序的通用能力(文件监视、可选文件、IFileProvider抽象)。同时它多目标编译支持netstandard2.0与 .NET Framework,兼容面广。
快速上手:从 INI 文件构建配置
根据 README.md 中的示例,读取 INI 配置只需三步:ConfigurationBuilder→AddIniFile→ 读取节与键值:
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是相对于ConfigurationBuilder的BasePath(默认为当前工作目录)解析的。若希望路径相对于程序集所在目录,可在构建前调用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会注册文件监视器,文件变化时自动触发配置重载。path为null或空字符串时抛出ArgumentException,错误消息为资源文件中的Error_InvalidFilePath("File path must be a non-empty string."),见 Strings.resx,测试ThrowExceptionWhenPassingNullAsFilePath与ThrowExceptionWhenPassingEmptyStringAsFilePath均有覆盖。
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。这意味着冒号同时承担"节嵌套分隔符"的职责。测试CanLoadValidIniFromStreamProvider与LoadKeyValuePairsFromValidIniFile均使用[Data:Inventory]与SubHeader:Provider=MySql验证了这种嵌套路径的展开。
3. 键值对
其余行必须以=分隔键和值,否则抛出FormatException(消息来自Error_UnrecognizedLineFormat:"Unrecognized line format: '{0}'.")。解析时:
- 键与值的首尾空白会被
Trim()去除; - 值如果被成对的双引号包裹(
"value"),双引号会被剥除; - 键不区分大小写,因为结果字典使用
StringComparer.OrdinalIgnoreCase。
测试ShouldRemoveLeadingAndTrailingWhiteSpacesFromKeyAndValue(\t key \t = \t value\t→key=value)与ShouldRemoveLeadingAndTrailingWhiteSpacesFromSectionName验证了空白处理;LoadKeyValuePairsFromValidIniFileWithQuotedValues验证了引号剥除。
关于引号有几个精确的边界行为(见测试DoubleQuoteIsPartOfValueIfNotPaired与DoubleQuoteIsPartOfValueIfAppearInTheMiddleOfValue):
- 只有值首字符与末字符都是
"时才剥除双引号; - 引号不成对(如
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):
- 用
StringComparer.OrdinalIgnoreCase创建Dictionary<string, string?>作为数据容器; - 用
StreamReader逐行读取,sectionPrefix变量记录当前节前缀(初始为空字符串); - 每行先
Trim():空白行continue;首字符为;/#//的行continue; - 若以
[开头且以]结尾,则把括号内的内容Trim()后拼接ConfigurationPath.KeyDelimiter更新sectionPrefix; - 否则查找
=分隔符,找不到就抛FormatException; - 拼接
key = sectionPrefix + 键部分.Trim(),值部分Trim(),再按规则处理成对双引号; - 若
data.ContainsKey(key)则抛重复键FormatException,否则写入字典。
IniConfigurationProvider与IniStreamConfigurationProvider都只是把Data = IniStreamConfigurationProvider.Read(stream)或Data = Read(stream)作为Load的实现(见 IniConfigurationProvider.cs),所以文件与流走的是同一套解析逻辑,任何格式行为在两个入口上完全一致。
IniConfigurationSource与IniStreamConfigurationSource分别是文件型与流型配置源的实现(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=false | FileNotFoundException | "The configuration file '{path}' was not found and is not optional. ..." |
行内无=分隔符 | FormatException | "Unrecognized line format: '{0}'."(Error_UnrecognizedLineFormat) |
节头不完整(如[ConnectionString) | FormatException | 同上(测试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.
相关推荐
.NET 中的 INI 配置提供程序:用 Microsoft.Extensions.Configuration.Ini 读取 INI 文件
.NET 中的 INI 配置提供程序:用 Microsoft.Extensions.Configuration.Ini 读取 INI 文件 INI 是一种古老但
语言运行时标准库JIT编译编译器.NET 配置系统实战:Microsoft.Extensions.Configuration.Json JSON 配置提供程序完全指南
.NET 配置系统实战:Microsoft.Extensions.Configuration.Json JSON 配置提供程序完全指南 导读 本文基于 .NET
语言运行时标准库JIT编译编译器.NET 运行时 User Secrets 配置提供程序(Microsoft.Extensions.Configuration.UserSecrets)原理与实战
.NET 运行时 User Secrets 配置提供程序(Microsoft.Extensions.Configuration.UserSecrets)原理与实
语言运行时标准库JIT编译编译器
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考