EF Core 7.0.2代码优先迁移时DbContext未配置数据库提供程序错误
执行迁移命令 dotnet ef migrations add InitialCreate --project PreventionCyber.DAL --startup-project PreventionCyber.Web 时,触发以下错误:
System.InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
问题分析及修复方案
1. 服务注册顺序错误
你在Program.cs中先执行了 var app = builder.Build();,之后才注册DbContext。但 builder.Build() 会完成服务容器的构建,后续的服务注册不会被纳入容器配置,导致EF迁移工具无法识别DbContext的数据库配置。
修复:将DbContext注册代码移到 builder.Build() 之前。
2. DbContext注册方式不当
使用 AddDbContext<DbContext, PCDbContext> 是间接注册基类DbContext的实现,但EF迁移工具会直接查找PCDbContext的服务注册,这种写法可能导致工具无法匹配到正确的配置。
修复:直接注册PCDbContext类型。
3. 保留无参构造函数风险
PCDbContext中存在无参构造函数,EF在某些场景下会优先使用这个构造函数实例化DbContext,而该构造函数没有配置数据库提供程序,从而触发错误。
修复:移除无参构造函数,强制使用带配置参数的构造函数。
修改后的代码
Program.cs
using Microsoft.EntityFrameworkCore; using PreventionCyber.DAL.Context; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); // 调整注册顺序,并直接注册PCDbContext builder.Services.AddDbContext<PCDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("PreventionCyber"))); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
PCDbContext.cs
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore; using PreventionCyber.DAL.Common; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace PreventionCyber.DAL.Context { public class PCDbContext : DbContext { // 移除无参构造函数,强制使用带配置的构造函数 public PCDbContext(DbContextOptions<PCDbContext> options): base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { foreach (var entity in modelBuilder.Model.GetEntityTypes()) { // Renommage clé primaire entity.GetProperty("Id") ?.SetColumnName($"{entity.ClrType.Name}Id"); } } public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken)) { OnBeforeSaving(); return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); } private void OnBeforeSaving() { var entries = ChangeTracker.Entries(); foreach (var entry in entries) { if (entry.Entity is BaseEntity trackable) { var now = DateTime.UtcNow; var user = GetCurrentUser(); switch (entry.State) { case EntityState.Modified: trackable.ModifieLe = now; trackable.ModifiePar = user; break; case EntityState.Added: trackable.CreeLe = now; trackable.CreePar = user; trackable.ModifieLe = now; trackable.ModifiePar = user; break; } } } } private string GetCurrentUser() { return "UserName"; // TODO implement your own logic } } }
额外说明
使用 builder.Configuration.GetConnectionString("PreventionCyber") 替代 GetValue<string> 是更规范的写法,它专门用于读取配置文件中ConnectionStrings节点下的内容。
内容的提问来源于stack exchange,提问作者Nathan Trouillet

