EFCore 不识别数据库提供程序

EFCore Not recognizing Database Provider

我有一个 .Net Core WebApplication 项目,其中上下文 Class 在 Class 库中。如果我在 OnConfiguring(DbContextOptionsBuilder optionsBuilder) 方法中硬编码连接字符串,我可以生成迁移。因为让依赖注入管理上下文更好,所以我想将它添加到 Startup Class。但是,当我这样做时,出现以下错误:

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.

DbContext Class:

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");

    }
}

启动Class - ConfigureServices 方法

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();
}

这让我很难受,出现如下错误:

  • No database provider has been configured for this DbContext.
  • No design-time services were found.
  • The server was not found or was not accessible.

但我最终得到了一个相当简单的 solution/work-around:

  • 在您的解决方案(或命令行)中设置默认 startup-project

  • 在你的Startup.csadd 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 ...
          }
    
  • 在您的连接字符串中使用 IP 地址和端口号(受 this corefx issue), instead of the server-name/dns (FWIW: query to get IP 启发)。 所以现在我的 appsettings.Development.json:

    中有了这个

    “连接字符串”:{ “MyConnectionStringName”:“数据源=10.1.2.3,1433;初始目录=MyCatalog;集成安全性=SSPI” }

TLDR:其他建议

我发现了很多其他建议,我会提到一些看起来很有趣的建议。也许它会对其他人有所帮助:

命令行中的项目名称

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

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> 

从代码迁移

也可以在 StartUp 调用迁移 , "对于具有本地数据库的应用"。 (我想否则 运行 在多个节点上,可能会同时启动多个运行时迁移并发问题?)

myDbContext.Database.Migrate();

在 Main.cs

中设置 DbContext

This EntityFrameworkCore issue 状态:

The problem is that when EF calls either CreateWebHostBuilder or BuildWebHost it does so without running Main. (This is intentional because EF needs to build the model and use the DbContext without starting the application.) This means that when EF invokes on of these methods the static IConfiguration property is still null--since it is only set in Main. So, you'll need to either make sure that IConfiguration is set/handled when EF calls one of these methods, or use IDesignTimeDbContextFactory.

这对我来说不是必需的,我猜是因为 .Net Core 2 loads the configuration behind the scenes.

将 IDesignTimeDbContextFactory 与环境变量一起使用

This EntityFrameworkCore issue 状态:

The typical way to do this is to read a file, an environment variable, or similar inside IDesignTimeDbContextFactory.

我觉得这太过分了。

在设计时创建 DbContext

Microsoft documentation 提及:

Some of the EF Core Tools commands (for example, the Migrations commands) require a derived DbContext instance to be created at design time in order to gather details about the application's entity types and how they map to a database schema.

他们提到了这些提供设计时 DbContext 的方法:

  • 来自应用程序服务:对于 ASP.NET 作为启动项目的核心应用程序:

The tools try to obtain the DbContext object from the application's service provider. [...] The tools first try to obtain the service provider by invoking Program.BuildWebHost() [JP: or CreateWebHostBuilder] and accessing the IWebHost.Services property. The DbContext itself and any dependencies in its constructor need to be registered as services in the application's service provider. This can be easily achieved by having a constructor on the DbContext that takes an instance of DbContextOptions<TContext> as an argument and using the AddDbContext<TContext> method.

  • 使用不带参数的构造函数

If the DbContext can't be obtained from the application service provider, the tools look for the derived DbContext type inside the project. Then they try to create an instance using a constructor with no parameters. This can be the default constructor if the DbContext is configured using the OnConfiguring method.

  • 来自设计时工厂

You can also tell the tools how to create your DbContext by implementing the IDesignTimeDbContextFactory<TContext> interface: If a class implementing this interface is found in either the same project as the derived DbContext or in the application's startup project, the tools bypass the other ways of creating the DbContext and use the design-time factory.

当我在将 dbcontext 添加到服务时提供选项和 dbcontext 时遇到了同样的问题,例如:-

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

然后我添加了没有选项的 dbcontext,它解决了我的问题

services.AddDbContext<TodoContext>();

我还必须将 OnConfiguring 方法添加到 dbcontext class 以便上下文可以访问连接字符串:-

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer("Server=(localdb)\mssqllocaldb;Database=IdenDB;Trusted_Connection=True;MultipleActiveResultSets=true");
}

我不确定这是否是正确的做事方式,因为我刚开始使用核心,但 是一个更详细地解释这个问题的答案