EFCore 无法识别数据库提供程序 [英] EFCore Not recognizing Database Provider
问题描述
我有一个 .Net Core WebApplication 项目,其中 Context 类位于类库中.如果我在 OnConfiguring(DbContextOptionsBuilder optionsBuilder) 方法中硬编码连接字符串,我可以生成迁移.由于最好让依赖注入管理上下文,因此我想将其添加到启动类中.但是,当我这样做时,我收到以下错误:
I have a .Net Core WebApplication Project in which the Context Class is in a Class Library. If I hard code the connection string in the OnConfiguring(DbContextOptionsBuilder optionsBuilder) method I can generate migrations. Since it is better to let dependency injection manage the context I would like to add this to the Startup Class. However when I do I get the following error:
没有为此 DbContext 配置数据库提供程序.可以通过覆盖 DbContext.OnConfiguring 方法或在应用程序服务提供者上使用 AddDbContext 来配置提供者.如果使用 AddDbContext,还要确保您的 DbContext 类型在其构造函数中接受 DbContextOptions 对象并将其传递给 DbContext 的基本构造函数.
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 类:
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");
}
}
启动类 - 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();
}
推荐答案
这让我很难受,得到如下错误:
This bit me hard, getting errors like:
没有为此 DbContext 配置数据库提供程序.
未找到设计时服务.
未找到或无法访问服务器.
但我最终得到了一个相当简单的解决方案/变通方法:
But I ended up with a fairly simple solution/work-around:
在您的解决方案(或命令行)中设置默认的startup-project
在您的 Startup.cs
添加迁移项目:
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 ...
}
在您的连接字符串中使用 IP 地址和端口号(灵感来自 这个 corefx 问题),而不是服务器名称/dns(FWIW:查询获取 IP).所以现在我在我的 appsettings.Development.json
中有这个:
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
:
连接字符串":{MyConnectionStringName":数据源=10.1.2.3,1433;初始目录=我的目录;集成安全=SSPI"}
"ConnectionStrings": { "MyConnectionStringName": "Data Source=10.1.2.3,1433; Initial Catalog=MyCatalog; Integrated Security=SSPI" }
我发现了很多其他的建议,我会提到一些看起来很有趣的.也许它会帮助别人:
I found a lot of other suggestions, and I will mention a few that seemed interesting. Maybe it will help someone else:
在命令行中提及启动项目和迁移项目:
Mention startup-project and migration-project in command-line:
Update-Database -Verbose -Project x.Data -StartupProject x.Web
添加(来自评论):这也适用于 dotnet CLI:
Addition (from the comments): This also works from the 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>
从代码迁移
还可以在 启动,对于具有本地数据库的应用".(我想否则会在多个节点上运行,可能会同时启动多个运行时迁移并出现并发问题?)
Migrate from code
One can also call migrations at StartUp , "for apps with a local database". (I guess otherwise running on multiple nodes, may start multiple runtime migrations at the same time with concurrency issues?)
myDbContext.Database.Migrate();
在 Main.cs 中设置 DbContext
问题是当 EF 调用 CreateWebHostBuilder
或BuildWebHost
它不需要运行 Main
.(这是故意的因为 EF 需要构建模型并使用 DbContext
而没有启动应用程序.)这意味着当 EF 调用这些方法静态 IConfiguration
属性仍然为空——因为它仅在 Main
中设置.因此,您需要确保IConfiguration
在 EF 调用这些方法之一时设置/处理,或使用 IDesignTimeDbContextFactory
.
The problem is that when EF calls either
CreateWebHostBuilder
orBuildWebHost
it does so without runningMain
. (This is intentional because EF needs to build the model and use theDbContext
without starting the application.) This means that when EF invokes on of these methods the staticIConfiguration
property is still null--since it is only set inMain
. So, you'll need to either make sure thatIConfiguration
is set/handled when EF calls one of these methods, or useIDesignTimeDbContextFactory
.
这对我来说不是必需的,我猜是因为 .Net Core 2 在后台加载配置.
This is not necessary for me, I guess because .Net Core 2 loads the configuration behind the scenes.
执行此操作的典型方法是在 IDesignTimeDbContextFactory
中读取文件、环境变量或类似内容.
The typical way to do this is to read a file, an environment variable, or similar inside
IDesignTimeDbContextFactory
.
这对我来说似乎太过分了.
This seems too much a hack to me.
微软文档提到:
某些 EF Core 工具命令(例如,Migrations命令)需要在设计时创建派生的 DbContext 实例时间以收集有关应用程序实体类型的详细信息以及它们如何映射到数据库架构.
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 的方法:
They mention these ways to provide this design time DbContext:
- 来自应用程序服务:对于作为启动项目的 ASP.NET Core 应用程序:
这些工具尝试从应用程序中获取 DbContext
对象服务提供者.[...] 工具首先尝试获取服务通过调用 Program.BuildWebHost()
[JP: 或CreateWebHostBuilder
] 并访问 IWebHost.Services
财产.DbContext
本身及其中的任何依赖项构造函数需要在应用程序中注册为服务服务提供者.这可以通过构造函数轻松实现在采用实例的 DbContext
上DbContextOptions
作为参数并使用AddDbContext
方法.
The tools try to obtain the
DbContext
object from the application's service provider. [...] The tools first try to obtain the service provider by invokingProgram.BuildWebHost()
[JP: orCreateWebHostBuilder
] and accessing theIWebHost.Services
property. TheDbContext
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 theDbContext
that takes an instance ofDbContextOptions<TContext>
as an argument and using theAddDbContext<TContext>
method.
- 使用不带参数的构造函数
- 来自设计时工厂
如果DbContext
无法从应用服务中获取提供者,这些工具在内部寻找派生的 DbContext
类型项目.然后他们尝试使用构造函数创建一个实例没有参数.如果 DbContext
使用 OnConfiguring
方法进行配置.
If the
DbContext
can't be obtained from the application service provider, the tools look for the derivedDbContext
type inside the project. Then they try to create an instance using a constructor with no parameters. This can be the default constructor if theDbContext
is configured using theOnConfiguring
method.
您还可以通过以下方式告诉工具如何创建您的 DbContext
实现 IDesignTimeDbContextFactory
接口:如果实现此接口的类可以在相同的项目作为派生的 DbContext 或在应用程序的启动项目,这些工具绕过了创建 DbContext
的其他方式并使用设计时工厂.
You can also tell the tools how to create your
DbContext
by implementing theIDesignTimeDbContextFactory<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 theDbContext
and use the design-time factory.
这篇关于EFCore 无法识别数据库提供程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!