为什么 Environment.GetEnvironmentVariable
读不到 .env 文件里的变量?
因为
.env文件不是操作系统环境变量,它只是项目里一个普通文本文件。C# 的
Environment.GetEnvironmentVariable只能读取系统或进程级已加载的环境变量,不会自动解析项目根目录下的
.env文件。
你需要手动读取、解析并注入到当前进程环境变量中(可选),或者直接用字典管理配置值。
不注入进程环境变量:更安全,避免污染全局状态,推荐用于大多数应用 调用Environment.SetEnvironmentVariable注入:后续调用
Environment.GetEnvironmentVariable才能生效,但要注意作用域(
EnvironmentVariableTarget.Process是默认且最常用)
.env文件需 UTF-8 编码(无 BOM),否则中文可能乱码
用 DotNetEnv
库最省事:支持注释、引号、多行值
DotNetEnv是目前 C# 生态中最接近 Node.js
dotenv行为的库,能正确处理带空格、等号、引号的值,也跳过以
#开头的注释行。
安装方式:
dotnet add package DotNetEnv
基础用法(推荐在
Program.cs或启动入口处执行):
DotNetEnv.Env.Load(); // 默认加载项目根目录下的 .env
如果
.env在其他位置(如
config/.env):
DotNetEnv.Env.Load("config/.env");
它会自动将每行 KEY=VALUE解析为环境变量,并调用
Environment.SetEnvironmentVariable重复 key 会覆盖前一个;空行和
#开头的行被忽略 不支持变量插值(如
BASE_URL=https://api.example.com+
API_URL=$BASE_URL/v1)
自己手写解析器:只依赖 System.IO
,适合轻量或受控场景
如果你不想引入第三方包,且
.env格式简单(无引号、无空格、无注释),可以用几行代码搞定:
var envPath = Path.Combine(AppContext.BaseDirectory, ".env");
if (File.Exists(envPath))
{
foreach (var line in File.ReadAllLines(envPath))
{
var trimmed = line.Trim();
if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith("#")) continue;
if (trimmed.Contains('='))
{
var parts = trimmed.Split(new char[] { '=' }, 2);
var key = parts[0].Trim();
var value = parts[1].Trim().Trim('"', '\'');
Environment.SetEnvironmentVariable(key, value, EnvironmentVariableTarget.Process);
}
}
}
注意用 Split(..., 2)防止值中含等号被错误切分
Trim('"', '\'') 去掉单/双引号包裹的值(常见于含空格的路径或 URL)
没做转义处理(如 \n、
\t),也不处理反斜杠续行 —— 这些属于高级需求,建议交给
DotNetEnv
ASP.NET Core 中怎么集成?别直接改 Environment
在 ASP.NET Core 项目里,更推荐用内置的
IConfiguration加载
.env,而不是往
Environment里塞变量。这样配置可被 DI 自动注入,也兼容
appsettings.json分层结构。
示例(
Program.cs):
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddEnvironmentVariables(); // 先加系统变量
// 再加 .env(需先解析成 IDictionary<string, string>)
var envVars = DotNetEnv.Env.Parse(File.ReadAllText(".env"));
builder.Configuration.AddInMemoryCollection(envVars);
这样 IConfiguration["DB_HOST"]就能取到
.env里的值 优先级由添加顺序决定:后添加的配置源会覆盖前面同名 key 不要在
AddEnvironmentVariables()之后再调用
DotNetEnv.Env.Load(),否则可能造成冗余注入
真正麻烦的不是读取,而是确保
.env不被提交到 Git、不同环境用不同文件(如
.env.development)、以及敏感值不硬编码 —— 这些得靠 CI/CD 流程和团队约定来兜底。
