【ASP.NET CORE】 4. 集成配置系统、分层架构
本系列专栏基于杨中科老师的《ASP.NET Core技术内幕与项目实战》,本人记录梳理的学习笔记,有部分的增补和省略。更全面系统的讲解,请看杨老师的视频课:【.NET教程,.Net Core视频教程,杨中科主讲】。
一、配置系统使用
ASP.NET Core 配置系统采用提供者模式,支持多源配置、热更新、环境隔离,是项目开发的核心基础,以下是完整使用指南。
1. 默认配置提供者(加载优先级:后加载覆盖先加载)
ASP.NETCore 主机默认按固定顺序加载配置源,后续配置会覆盖同名键值,生产环境需严格遵循优先级规范:
- 加载已注入的
IConfiguration根配置对象; - 加载项目根目录的
appsettings.json(基础公共配置); - 加载环境差异化配置
appsettings.{Environment}.json(环境专属配置); - 开发环境专属:加载用户机密配置(secrets.json,本地敏感信息);
- 加载系统环境变量(容器 / 服务器级配置);
- 加载命令行参数(临时启动配置,优先级最高)。
2. 运行环境模式(环境隔离核心)
ASP.NETCore 通过环境变量区分运行环境,实现开发 / 测试 / 生产配置完全隔离,避免配置混淆引发线上问题。
- 环境变量名:固定为
ASPNETCORE_ENVIRONMENT - 官方推荐值:
Development:开发环境(本地调试,启用 Swagger、用户机密)Staging:测试 / 预发环境(模拟生产验证)Production:生产环境(禁用调试、开启日志压缩)
- 环境判断方法:
- 获取环境名称:
app.Environment.EnvironmentName - 快速判断:
app.Environment.IsDevelopment()/IsStaging()/IsProduction()
- 获取环境名称:
示例
开发环境仅启用 Swagger
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); // 仅开发环境加载Swagger,生产环境自动禁用 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.Run();3. 用户机密
核心作用:存储数据库连接串、密钥、Token 等不能提交到 Git / 服务器的本地机密信息,仅开发环境使用,绝对不用于生产环境。
使用
- 右键ASP.NETCore 项目 → 选择【管理用户机密】,自动生成
secrets.json; - 机密文件物理路径:
- Windows:
C:\Users\用户名\AppData\Roaming\Microsoft\UserSecrets\{UserSecretsId}\secrets.json - Mac/Linux:
~/.microsoft/usersecrets/{UserSecretsId}/secrets.json
- Windows:
- 项目关联:通过项目文件
.csproj中的<UserSecretsId>唯一标识绑定,无需手动配置路径。
secrets.json 示例
(存储数据库连接串)
{ "ConnectionStrings": { "Default": "Data Source=.;Initial Catalog=demo1;Integrated Security=SSPI;TrustServerCertificate=true" } }4. 配置实战
(数据库配置 + Redis 集成)
生产环境推荐:核心配置存入专用数据库,连接串存入用户机密,实现配置中心化管理。依赖:Zack.AnyDBConfigProvider(轻量级数据库配置提供者)
步骤 1:注册数据库配置源
var builder = WebApplication.CreateBuilder(args); // 1. 从用户机密读取配置数据库连接串 string configConnStr = builder.Configuration.GetConnectionString("Default"); // 2. 注册数据库配置提供者(加载Redis、Smtp等核心配置) builder.Configuration.AddDBConfig(opts => { opts.DbType = DbType.SqlServer; // 数据库类型 opts.ConnectionString = configConnStr; // 配置库连接串 }); // 3. 读取配置并绑定为强类型对象(推荐:类型安全,避免魔法字符串) var redisConfig = builder.Configuration.GetSection("Redis").Get<RedisConfig>(); var smtpConfig = builder.Configuration.GetSection("Smtp").Get<SmtpConfig>(); // 4. 注册Redis服务(程序启动时初始化,注入DI容器) builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = redisConfig.ConnectionString; options.InstanceName = "DemoRedis"; }); // 注册配置对象到DI,方便控制器使用 builder.Services.AddSingleton(redisConfig); builder.Services.AddSingleton(smtpConfig);步骤 2:强类型配置实体(推荐)
// Redis配置 public class RedisConfig { public string ConnectionString { get; set; } public int DefaultDB { get; set; } } // Smtp邮件配置 public class SmtpConfig { public string Server { get; set; } public int Port { get; set; } public string UserName { get; set; } public string Password { get; set; } }步骤 3:控制器中读取配置
[ApiController] [Route("api/[controller]")] public class ConfigController : ControllerBase { private readonly SmtpConfig _smtpConfig; // 依赖注入配置对象 public ConfigController(SmtpConfig smtpConfig) { _smtpConfig = smtpConfig; } [HttpGet("smtp")] public IActionResult GetSmtpConfig() { // 返回Smtp配置到前端(生产环境隐藏敏感字段) return Ok(new { Server = _smtpConfig.Server, Port = _smtpConfig.Port }); } }二、项目分层
大型项目禁止所有代码写在 Web 项目中,推荐领域驱动分层核心思想:数据层独立、Web 层仅负责配置和入口。
标准分层结构
- XXX.EFCore:数据访问层(实体类、EF 上下文、配置映射)
- XXX.WebApi:Web 入口层(控制器、配置、依赖注入)
- (扩展)XXX.Application:业务逻辑层;XXX.Domain:领域模型层
实战
Books 项目分层实现
1. 创建数据访问类库:BooksEFCore
- 新建【.NET 类库项目】,命名
BooksEFCore - 安装 NuGet 包:
Install-Package Microsoft.EntityFrameworkCore.Relational Install-Package Microsoft.EntityFrameworkCore.Tools Install-Package Microsoft.EntityFrameworkCore.SqlServer
2. 编写实体类 + 配置类
// 实体类:Book.cs public class Book { public long Id { get; set; } public string Title { get; set; } public string AuthorName { get; set; } public decimal Price { get; set; } public DateTime PubDate { get; set; } } // 配置类:FluentAPI配置(推荐:分离实体和配置) public class BookConfig : IEntityTypeConfiguration<Book> { public void Configure(EntityTypeBuilder<Book> builder) { builder.ToTable("T_Books"); // 表名 builder.Property(b => b.Title).IsRequired().HasMaxLength(200); builder.Property(b => b.AuthorName).IsRequired().HasMaxLength(50); } }3. 编写 DbContext
通过构造函数注入DbContextOptions,不重写 OnConfiguring,实现数据库配置与上下文解耦。
public class MyDbContext : DbContext { // 构造函数注入配置(Web层统一配置) public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { } public DbSet<Book> Books => Set<Book>(); // 加载配置 protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 批量加载当前程序集的所有配置类 modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly); } }// 不要在上下文硬编码连接串!无法切换数据库、无法测试 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("连接串"); }4. Web 层配置
- 添加对
BooksEFCore项目的引用 - 注册 DbContext 到依赖注入:
var builder = WebApplication.CreateBuilder(args); // 从配置系统读取连接串 string connStr = builder.Configuration.GetConnectionString("Default"); // 注册DbContext builder.Services.AddDbContext<MyDbContext>(opt => { opt.UseSqlServer(connStr); // 可轻松切换为UseMySQL/UsePostgre });5. 控制器中使用 DbContext
[ApiController] [Route("api/[controller]")] public class BookController : ControllerBase { private readonly MyDbContext _dbContext; // 依赖注入上下文 public BookController(MyDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public async Task<IActionResult> GetBooks() { var books = await _dbContext.Books.ToListAsync(); return Ok(books); } }6. 多项目迁移避坑
问题:迁移脚本生成时,EF 无法直接读取 Web 层配置,会报错。
解决方案:在BooksEFCore中创建设计时上下文工厂(IDesignTimeDbContextFactory),仅用于生成迁移脚本。
/// <summary> /// EF迁移设计时工厂(仅开发环境生成迁移使用) /// </summary> internal class MyDbContextDesignFactory : IDesignTimeDbContextFactory<MyDbContext> { public MyDbContext CreateDbContext(string[] args) { var builder = new DbContextOptionsBuilder<MyDbContext>(); // 开发环境数据库连接串(可读取环境变量) string connStr = Environment.GetEnvironmentVariable("DefaultConnection") ?? "Data Source=.;Initial Catalog=demo666;Integrated Security=SSPI;TrustServerCertificate=true"; builder.UseSqlServer(connStr); return new MyDbContext(builder.Options); } }7. 生成迁移脚本
- 将
BooksEFCore设置为启动项目 - 打开【程序包管理器控制台】,默认项目选择
BooksEFCore - 执行命令:
Add-Migration InitBook // 生成迁移文件 Update-Database // 同步到数据库
批量注册多DbContext
大型项目采用小上下文(一个上下文对应一组业务表),手动注册太繁琐,用反射批量注册:依赖:Zack.Infrastructure
Install-Package Zack.Infrastructure批量注册代码:
// 扫描BooksEFCore程序集,自动注册所有DbContext builder.Services.AddDbContexts(typeof(MyDbContext).Assembly);总结
- 配置系统:默认多源加载,优先级「命令行 > 环境变量 > 用户机密 > appsettings」,机密信息用用户机密,核心配置用数据库中心化管理;
- 项目分层:数据层独立封装实体和上下文,Web 层统一配置和 DI,遵循关注点分离;
- EF 最佳实践:构造函数注入 Options,用设计时工厂解决多项目迁移问题,避免硬编码配置;
- 生产规范:开发环境用用户机密,生产环境用环境变量 / 配置中心,绝不泄露敏感信息。
