如何在分部DbContext的OnConfiguring中访问依赖注入服务?
关于DbContext分部类构造注入与OnConfiguring的问题解答
1. DI如何选择正确的构造函数?
ASP.NET Core默认DI容器的构造函数选择逻辑很明确:
- 优先挑选参数数量最多,且所有参数都能从容器中成功解析的构造函数。
- 只有当带参数的构造函数无法被解析(比如依赖未注册)时,才会回退到无参数构造函数。
你的场景中,只要已经通过builder.Services.AddHttpContextAccessor()把IHttpContextAccessor注册到DI容器,容器就会自动选择你在分部类中定义的带参数构造函数。脚手架生成的空构造函数只会在你手动new MyDbContext()时才会被调用——这种手动实例化的场景在DI托管的上下文流程里几乎不会出现,把字段设为可空只是为了兼容极端情况,完全不影响DI的正常工作。
2. OnConfiguring中使用GetService抛出上下文未配置完成异常的原因及解决办法
原因
OnConfiguring是EF Core初始化上下文时的早期回调,此时上下文内部的ServiceProvider还未完全初始化完成,直接调用GetService根本拿不到有效服务,自然会触发未配置完成的异常。
正确做法
通过构造注入提前将IHttpContextAccessor注入并存储为私有字段,在OnConfiguring中直接使用这个字段即可,完全不需要在方法内部调用GetService。
完整代码示例
脚手架生成的分部类(简化版)
public partial class MyDbContext : DbContext { // 脚手架生成的无参数构造 public MyDbContext() { } // 自动生成的DbSet定义 public DbSet<Product> Products { get; set; } = null!; // 其他DbSet... }
自定义分部类实现构造注入与OnConfiguring
public partial class MyDbContext { private readonly IHttpContextAccessor? _httpContextAccessor; // 构造注入IHttpContextAccessor public MyDbContext(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // 仅当未通过DI配置Options时才执行自定义配置(避免重复配置) if (!optionsBuilder.IsConfigured) { // 从HttpContext获取所需信息,比如租户ID var tenantId = _httpContextAccessor?.HttpContext?.User.Claims .FirstOrDefault(c => c.Type == "TenantId")?.Value; // 根据业务逻辑获取对应连接字符串 var connectionString = GetConnectionString(tenantId); optionsBuilder.UseSqlServer(connectionString); } } // 示例:根据租户ID获取连接字符串 private string GetConnectionString(string? tenantId) { return tenantId switch { "Tenant001" => "Server=.;Database=Tenant001Db;Trusted_Connection=True;", "Tenant002" => "Server=.;Database=Tenant002Db;Trusted_Connection=True;", _ => "Server=.;Database=DefaultDb;Trusted_Connection=True;" }; } }
DI注册代码(Program.cs)
var builder = WebApplication.CreateBuilder(args); // 注册HttpContextAccessor builder.Services.AddHttpContextAccessor(); // 注册DbContext,DI会自动使用带参数的构造函数 builder.Services.AddDbContext<MyDbContext>(); // 其他服务注册... var app = builder.Build(); // 中间件配置... app.Run();
内容的提问来源于stack exchange,提问作者katkak
相关产品推荐
相关产品推荐

