在 Entity Framework Core 中自动创建数据库

Posted

技术标签:

【中文标题】在 Entity Framework Core 中自动创建数据库【英文标题】:auto create database in Entity Framework Core 【发布时间】:2017-07-10 09:02:41 【问题描述】:

我正在移植到 .NET 核心的应用程序将使用带有 SQLite 的新 EF Core。我想在应用程序首次运行时自动创建数据库和表结构。根据 EF 核心文档,这是使用手动命令完成的

dotnet ef migrations add MyFirstMigration

dotnet ef database update

但是,我不希望最终用户输入这些命令,而是希望让应用程序创建并设置数据库以供首次使用。对于 EF 6,有类似

的功能
Database.SetInitializer(new CreateDatabaseIfNotExists<MyContext>());

但在 EF Core 中,这些似乎不存在。我找不到任何与 EF 核心等效的示例或文档,并且在 EF 核心文档的缺失功能列表中没有提到它。我已经设置了模型类,因此我可以编写一些代码来基于模型初始化数据库,但如果框架自动执行此操作会更容易。我不想自动构建模型或迁移,只需在新数据库上创建表结构。

我在这里遗漏了什么还是 EF 核心中缺少自动创建表功能?

【问题讨论】:

【参考方案1】:

如果您已经创建了迁移,您可以在 Startup.cs 中执行它们,如下所示。

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 
      using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
      
            var context = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            context.Database.Migrate();
      
      
      ...

这将使用您添加的迁移创建数据库和表。

如果您不使用实体框架迁移,而只需要在第一次运行时完全按照上下文类中的方式创建 DbContext 模型,那么您可以使用:

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 
      using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
      
            var context = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            context.Database.EnsureCreated();
      
      
      ...

相反。

如果您需要在确保创建数据库之前删除数据库,请致电:

            context.Database.EnsureDeleted();

就在您致电之前EnsureCreated()

改编自:http://docs.identityserver.io/en/latest/quickstarts/7_entity_framework.html?highlight=entity

【讨论】:

我对 EF 还是很陌生,我创建了使用 EF 6 代码定义数据结构的类,首先使用当前数据库文件从 Visual Studio 中“从数据库创建”。然后我将这些剪切/粘贴到新的 dotnet core VS 解决方案中 - 所以我猜这些不是迁移。这是否意味着我必须先创建一个迁移文件才能使用上述代码? 对不起,我的回答有误,如果你没有使用迁移,可以使用命令 context.Database.EnsureCreated()/EnsureDeleted(),更多细节在blogs.msdn.microsoft.com/dotnet/2016/09/29/… 如果您需要两种解决方案怎么办?我们刚刚将我们的应用程序移植到 UWP 并开始使用 EF Core,这意味着一些用户需要从头开始创建 DB,而另一些用户已经拥有 DB。是否有某种方法可以确保第一次迁移仅在初始表不存在的情况下创建它们?您的回答似乎没有涵盖这一点,还是我遗漏了什么? 看起来 IApplicationBuilder 适用于 Web 应用程序,是否有可能从 C# azure 函数中做同样的事情?【参考方案2】:

我的回答与 Ricardo 的回答非常相似,但我觉得我的方法更简单一些,因为他的 using 函数中发生了太多事情,我什至不确定它在低级。

因此,对于那些想要一个简单而干净的解决方案来为您创建数据库的人来说,您可以确切地知道幕后发生的事情,这是给您的:

public Startup(IHostingEnvironment env)

    using (var client = new TargetsContext())
    
        client.Database.EnsureCreated();
    

这几乎意味着在您创建的DbContext 中(在这种情况下,我的称为TargetsContext),您可以使用DbContext 的实例来确保在类中定义的表是Startup.cs 在您的应用程序中运行时创建。

【讨论】:

正如来自here 的信息:EnsureCreated 完全绕过迁移并仅为您创建架构,您不能将其与迁移混合。 EnsureCreated 专为测试或快速原型设计而设计,您可以每次都删除和重新创建数据库。如果您正在使用迁移并希望在应用启动时自动应用它们,那么您可以改用 context.Database.Migrate() 这回答了我为什么我的连接字符串不起作用的困惑...... :( 所以数据库不会自动创建,你必须指定我在 DbContext 构造函数中完成的 Database.EnsureCreated()。非常感谢,让我摆脱了 3 天的困境。X_X 只是想指出,我只是尝试将其粘贴到我的启动文件中,VS2019 告诉我 IHostingEnvironment 现在已弃用,推荐的替代方法是 Microsoft.AspNetCore.Hosting.IWebHostEnvironment【参考方案3】:

如果通过 Startup.cs 中的 Configure 参数列表获取上下文,则可以改为:

public void Configure(IApplicationBuilder app, IHostingEnvironment env,  LoggerFactory loggerFactory,
    ApplicationDbContext context)
 
      context.Database.Migrate();
      ...

【讨论】:

恕我直言,这是最好的解决方案:您不必关心任何服务甚至数据库上下文,您只需使用通过依赖注入获得的上下文即可。不错!【参考方案4】:

对于 EF Core 2.0+,我不得不采用不同的方法,因为他们更改了 API。截至 2019 年 3 月 Microsoft recommends,您将数据库迁移代码放在应用程序入口类中,但在 WebHost 构建代码之外。

public class Program

    public static void Main(string[] args)
    
        var host = CreateWebHostBuilder(args).Build();
        using (var serviceScope = host.Services.CreateScope())
        
            var context = serviceScope.ServiceProvider.GetRequiredService<PersonContext>();
            context.Database.Migrate();
        
        host.Run();
    

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();

【讨论】:

请记住,如果您在运行时运行迁移,则需要使用具有写入数据库模式的权限的数据库连接。使用这种方法时,建议对查询使用与运行迁移不同的连接。如果 SQL 注入攻击以某种方式通过,并且您正在使用具有 DCL 或 DDL 权限的连接字符串进行查询,则它是一个潜在的攻击向量。 geeksforgeeks.org/sql-ddl-dql-dml-dcl-tcl-commands【参考方案5】:

如果您尚未创建迁移,则有 2 个选项

1.从应用程序Main创建数据库和表:

var context = services.GetRequiredService<YourRepository>();
context.Database.EnsureCreated();

2.如果数据库已经存在,则创建表:

var context = services.GetRequiredService<YourRepository>();
context.Database.EnsureCreated();
RelationalDatabaseCreator databaseCreator =
(RelationalDatabaseCreator)context.Database.GetService<IDatabaseCreator>();
databaseCreator.CreateTables();

感谢布比的answer

【讨论】:

你们的服务是什么? 我正在阅读的所有文档都显示了有关迁移的大量警告,称“不要使用 EnsureCreatedEnsureDeletedDatabase.Migrate 将失败”【参考方案6】:

如果您希望同时使用 EnsureCreated 和 Migrate,请使用以下代码:

     using (var context = new YourDbContext())
            
                if (context.Database.EnsureCreated())
                
                    //auto migration when database created first time

                    //add migration history table

                    string createEFMigrationsHistoryCommand = $@"
USE [context.Database.GetDbConnection().Database];
SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
CREATE TABLE [dbo].[__EFMigrationsHistory](
    [MigrationId] [nvarchar](150) NOT NULL,
    [ProductVersion] [nvarchar](32) NOT NULL,
 CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY CLUSTERED 
(
    [MigrationId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY];
";
                    context.Database.ExecuteSqlRaw(createEFMigrationsHistoryCommand);

                    //insert all of migrations
                    var dbAssebmly = context.GetType().GetAssembly();
                    foreach (var item in dbAssebmly.GetTypes())
                    
                        if (item.BaseType == typeof(Migration))
                        
                            string migrationName = item.GetCustomAttributes<MigrationAttribute>().First().Id;
                            var version = typeof(Migration).Assembly.GetName().Version;
                            string efVersion = $"version.Major.version.Minor.version.Build";
                            context.Database.ExecuteSqlRaw("INSERT INTO __EFMigrationsHistory(MigrationId,ProductVersion) VALUES (0,1)", migrationName, efVersion);
                        
                    
                
                context.Database.Migrate();
            

【讨论】:

以上是关于在 Entity Framework Core 中自动创建数据库的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Entity Framework Core 中运行存储过程?

如何在 Entity Framework Core 中运行存储过程?

什么是自有实体?何时以及为什么在 Entity Framework Core 中使用 Owned Entity?

Entity Framework Core 中 TimeSpan 的总和

Entity Framework优化一:引发了“System.Data.Entity.Core.EntityCommandExecutionException”类型的异常

如何在 Entity Framework Core 2 中播种?