EFCore未识别数据库提供程序。

4
我有一个 .Net Core WebApplication 项目,其中 Context 类在一个类库中。如果我在 OnConfiguring(DbContextOptionsBuilder optionsBuilder) 方法中硬编码连接字符串,我可以生成迁移。由于最好让依赖注入管理上下文,我希望将其添加到 Startup 类中。然而,当我这样做时,我会得到以下错误:
“此 DbContext 没有配置数据库提供程序。可以通过覆盖 DbContext.OnConfiguring 方法或使用 AddDbContext 在应用程序服务提供程序上配置提供程序。如果使用了 AddDbContext,则还需确保您的 DbContext 类在其构造函数中接受 DbContextOptions 对象,并将其传递给 DbContext 的基类构造函数。”
DbContext类:
public class CustomerManagerContext : IdentityDbContext<User, Role, long, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>
{
    public CustomerManagerContext() { }
    public CustomerManagerContext(DbContextOptions<CustomerManagerContext> options) : base(options)
    {
    }

    //protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    //{
    //    base.OnConfiguring(optionsBuilder);
    //    optionsBuilder.UseSqlServer("SecretConnectionString");
    //}


    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<User>().ToTable("Users");
        builder.Entity<Role>().ToTable("Roles");
        builder.Entity<UserClaim>().ToTable("UserClaims");
        builder.Entity<UserRole>().ToTable("UserRoles");
        builder.Entity<UserLogin>().ToTable("UserLogins");
        builder.Entity<RoleClaim>().ToTable("RoleClaims");
        builder.Entity<UserToken>().ToTable("UserTokens");

    }
}

Startup Class - ConfigureServices Method

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<CustomerManagerContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
    );

    services.AddEntityFrameworkSqlServer()
        .AddDbContext<CustomerManagerContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<User, Role>()
        .AddEntityFrameworkStores<CustomerManagerContext>()
        .AddDefaultTokenProviders();
}

我不太确定,但是你同时使用AddDbContextAddEntityFrameworkSqlServer(并且在这里还调用了UseSqlServer)听起来完全不对。尝试注释掉对AddEntityFrameworkSqlServer的调用。 - Camilo Terevinto
@CamiloTerevinto 我已经尝试过将它们都注释掉,但都不起作用。如果我使用OnConfiguring,则设置可以正常工作。我一定漏掉了什么。 - Dblock247
您也可以尝试删除两个构造函数或至少是无参数的构造函数。 - Camilo Terevinto
如果我删除两个构造函数,我会得到相同的错误。如果我只删除无参数的构造函数,我会得到以下错误。在“CustomerManagerContext”上找不到无参数构造函数。要么在“CustomerManagerContext”中添加一个无参数构造函数,要么在与“CustomerManagerContext”相同的程序集中添加“IDbContextFactory<CustomerManagerContext>”的实现。 PM> add-migration Initial - Dblock247
@Dblock247:你不应该同时移除.AddDbContextAddEntityFrameworkSqlServer,只需移除AddEntityFrameworkSqlServer即可。我怀疑是因为它们都注册了两个DbContextOptionsBuilder实例,当依赖注入尝试通过provider.GetRequiredService<T>()解析它时,如果存在多个注册,它就会失败。多个注册只能通过GetRequiredServices(复数形式)来解决。 - Tseng
2个回答

8
这真的困扰了我很久,出现了以下错误信息:
  • 未为此 DbContext 配置数据库提供程序。
  • 没有找到设计时服务。
  • 服务器未找到或无法访问。
但是最终我找到了一个非常简单的解决方案/解决方法:
  • Set the default startup-project in your solution (or in your command line)

  • in your Startup.cs add the migration-project:

      public void ConfigureServices(IServiceCollection services)
      {
          var myDbContextAssemblyName = typeof(MyDbContext).Assembly.GetName().Name;
          var connectionString = Configuration.GetConnectionString(MyDbContext.ConnectionStringName);
          services.AddDbContext<MyDbContext>(options => options.UseSqlServer(
              connectionString,
              x => x.MigrationsAssembly(myDbContextAssemblyName)));
              // do more ...
          }
    
  • in your connection-string use the IP-address and port-number (inspired by this corefx issue), instead of the server-name/dns (FWIW: query to get IP). So now I have this in my appsettings.Development.json:

    "ConnectionStrings": { "MyConnectionStringName": "Data Source=10.1.2.3,1433; Initial Catalog=MyCatalog; Integrated Security=SSPI" }

简述:其他建议

我找到了很多其他的建议,这里提供几个看起来有趣的。也许它们能帮助其他人:

命令行中的项目名称

在命令行中提及启动项目和迁移项目:

Update-Database -Verbose -Project x.Data -StartupProject x.Web

补充(来自评论):这也适用于dotnet CLI:

dotnet ef migrations add <MIGRATION_NAME> --project <DATABASE_PROJECT_DIR> --startup-project <STARTUP_PROJECT_DIR> dotnet ef database update --project <DATABASE_PROJECT_DIR> --startup-project <STARTUP_PROJECT_DIR> 

从代码迁移

可以在启动时调用迁移来迁移本地数据库的应用程序,在其他多个节点上运行时,可能会出现并发问题导致同时启动多个运行时迁移。

(链接:Apply Migrations at Runtime)

myDbContext.Database.Migrate();

在 Main.cs 中设置 DbContext

这个 EntityFrameworkCore 的问题 指出:

问题在于 EF 在调用 CreateWebHostBuilder 或者 BuildWebHost 时,并没有运行 Main。(这是故意的, 因为 EF 需要构建模型并使用 DbContext,而不需要启动应用程序。) 这意味着当 EF 调用这些方法时,静态的 IConfiguration 属性仍然为空, 因为它只在 Main 中设置。所以,你需要确保在 EF 调用这些方法时设置或处理 IConfiguration,或者使用 IDesignTimeDbContextFactory

但对我来说似乎并不需要这样,因为 .Net Core 2 在幕后加载配置

使用带环境变量的 IDesignTimeDbContextFactory

这个 EntityFrameworkCore 的问题 指出:

通常的做法是在 IDesignTimeDbContextFactory 中读取一个文件,一个环境变量 或类似的东西。

对我来说,这似乎有点过度了。

设计时创建 DbContext

Microsoft 的文档 提到:

一些 EF Core 工具命令(例如迁移命令)需要在设计时创建一个派生的 DbContext 实例, 以便收集关于应用程序实体类型及其如何映射到数据库架构的详细信息。

他们提到以下几种方式来提供这个设计时 DbContext:

  • 从应用程序服务中:适用于 ASP.NET Core 应用作为启动项目
工具会尝试从应用程序的服务提供者中获取DbContext对象。[...] 工具首先尝试通过调用Program.BuildWebHost() [JP: 或CreateWebHostBuilder]并访问IWebHost.Services属性来获取服务提供者。 DbContext本身及其构造函数中的任何依赖项都需要在应用程序的服务提供者中注册为服务。这可以通过在DbContext上有一个构造函数来轻松实现,该构造函数采用DbContextOptions<TContext>的实例作为参数,并使用AddDbContext<TContext>方法。
  • 使用没有参数的构造函数
如果无法从应用程序服务提供者中获取DbContext,则工具将在项目内部查找派生的DbContext类型。然后它们尝试使用没有参数的构造函数创建实例。如果DbContext是使用OnConfiguring方法配置的,则可以使用默认构造函数。
  • 从设计时工厂中获取
您还可以通过实现IDesignTimeDbContextFactory<TContext>接口告诉工具如何创建您的DbContext:如果在派生的DbContext所在的同一项目或应用程序的启动项目中找到实现此接口的类,则工具将跳过创建DbContext的其他方式,并使用设计时工厂。

是的,IDesignTimeDbContextFactory<TContext> 对我也有用。谢谢,我忘记把我的解决方案放进去了。 - Dblock247
感谢您详细的回答。我如何在 OnConfiguring 之后调用 context.Database.Migrate?我有一个动态的租户配置,我希望在租户信息可用时生成数据库。也在这里发布了此问题。 - Shimmy Weitzhandler
1
@Shimmy,这似乎更像是一个新的相关问题而不是评论。但是,假设您有一个public class MyDbContext: DbContext,您可以显式调用一个单独的方法,例如:new MyDbContext().MyMigrationMethod(),其中new(隐式)调用this.OnConfiguring,并且MyMigrationMethod()应明确调用this.Database.Migrate()。当您知道您的租户相关配置已知并将被应用(例如从您的租户中调用它),但在使用您的数据库之前调用它。 - Yahoo Serious
1
这也适用于dotnet CLI:dotnet ef migrations add <MIGRATION_NAME> --project <DATABASE_PROJECT_DIR> --startup-project <STARTUP_PROJECT_DIR> dotnet ef database update --project <DATABASE_PROJECT_DIR> --startup-project <STARTUP_PROJECT_DIR> - DVS

1
当我在将DbContext添加到服务中时,同时提供选项时,我遇到了相同的问题,例如:-
services.AddDbContext<TodoContext>(options => 
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

然后我像下面这样添加了没有选项的dbcontext,问题就解决了

services.AddDbContext<TodoContext>();

我需要向 dbcontext 类中添加 OnConfiguring 方法,这样该上下文才能访问连接字符串。-
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=IdenDB;Trusted_Connection=True;MultipleActiveResultSets=true");
}

我不确定这是否是正确的做法,因为我刚开始学习核心,但这里有一个答案更详细地解释了这个问题。


你的 OnConfiguring 方法不会在每次创建上下文时被调用吗?那么这不会危及你在服务中设置的内容吗? - Ε Г И І И О

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接