如何在 Entity Framework Core 3.0 中播种?

Cla*_*u A 10 c# entity-framework-core asp.net-core-3.0

我正在尝试使用 ASP.NET CORE 3.0 和 EF Core 为数据库设置一些数据。

我已经创建了我的 DbContext 并根据文档在线资源,甚至EF Core 2.1 问题(我找不到关于这个主题的任何重大更改)。

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Band>().HasData(
            new Band()
            {
                Id = Guid.Parse("e96bf6d6-3c62-41a9-8ecf-1bd23af931c9"),
                Name = "SomeName",
                CreatedOn = new DateTime(1980, 2, 13),
                Description = "SomeDescription"
            });

        base.OnModelCreating(modelBuilder);           
    }
Run Code Online (Sandbox Code Playgroud)

这不符合我的预期:启动应用程序时没有任何种子(即使在调试期间从某处调用该方法)。

但是,如果我添加迁移,迁移包含相应的插入语句(这不是我正在寻找的种子类型)。

问题:在应用程序启动时执行数据库种子的正确方法是什么?

通过为数据库设置种子,我的意思是我希望每次启动应用程序时都可以在某些表中确保某些数据。


我可以选择创建种子类并在使用自定义代码的 Database.Migrate 之后处理它,但这似乎是一种解决方法,因为文档指定应使用 OnModelCreating 来种子数据)。


因此,在阅读答案并重新阅读文档后,我的理解是,“种子”是指可以在数据模型旁边进行的“初始化”(这就是为什么感觉很奇怪 - 将模型创建与数据播种部分)。

Ali*_*eza 18

如果您有复杂的种子数据,默认 EF 核心功能不是一个好主意。例如,您无法根据您的配置或系统环境添加种子数据。

我正在使用自定义服务和依赖项注入来添加我的种子数据,并在应用程序启动时为上下文应用任何挂起的迁移。生病分享我的定制服务希望它有帮助:

IDbInitializer.cs

    public interface IDbInitializer
    {
        /// <summary>
        /// Applies any pending migrations for the context to the database.
        /// Will create the database if it does not already exist.
        /// </summary>
        void Initialize();

        /// <summary>
        /// Adds some default values to the Db
        /// </summary>
        void SeedData();
    }
Run Code Online (Sandbox Code Playgroud)

数据库初始化程序

    public class DbInitializer : IDbInitializer {
        private readonly IServiceScopeFactory _scopeFactory;

        public DbInitializer (IServiceScopeFactory scopeFactory) {
            this._scopeFactory = scopeFactory;
        }

        public void Initialize () {
            using (var serviceScope = _scopeFactory.CreateScope ()) {
                using (var context = serviceScope.ServiceProvider.GetService<AppDbContext> ()) {
                    context.Database.Migrate ();
                }
            }
        }

        public void SeedData () {
            using (var serviceScope = _scopeFactory.CreateScope ()) {
                using (var context = serviceScope.ServiceProvider.GetService<AppDbContext> ()) {

                    //add admin user
                    if (!context.Users.Any ()) {
                        var adminUser = new User {
                            IsActive = true,
                            Username = "admin",
                            Password = "admin1234", // should be hash
                            SerialNumber = Guid.NewGuid ().ToString ()
                        };
                        context.Users.Add (adminUser);
                    }

                    context.SaveChanges ();
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

要使用此服务,您可以将其添加到您的服务集合中:

 // StartUp.cs -- ConfigureServices method
 services.AddScoped<IDbInitializer, DbInitializer> ()
Run Code Online (Sandbox Code Playgroud)

因为我想在每次程序启动时都使用此服务,所以我以这种方式使用注入服务:

 // StartUp.cs -- Configure method
         var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory> ();
         using (var scope = scopeFactory.CreateScope ()) {
            var dbInitializer = scope.ServiceProvider.GetService<IDbInitializer> ();
            dbInitializer.Initialize ();
            dbInitializer.SeedData ();
         }

Run Code Online (Sandbox Code Playgroud)


Cym*_*lis 8

我认为这不是OnModelCreating()为您的数据库提供种子的最佳场所。我认为这完全取决于您希望播种逻辑何时运行。您说过无论您的数据库是否有迁移更改,您都希望在应用程序启动时运行种子。

我将创建一个扩展方法来挂钩Configure()Startup.cs 类中的方法:

启动.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.MigrateAndSeedDb(development: true);
            }
            else
            {
                 app.MigrateAndSeedDb(development: false);
            }           

            app.UseHttpsRedirection();
 ...
Run Code Online (Sandbox Code Playgroud)

MigrateAndSeedDb.cs

 public static void MigrateAndSeedDb(this IApplicationBuilder app, bool development = false)
        {
            using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
            using (var context = serviceScope.ServiceProvider.GetService<GatewayDbContext>())
            {
                //your development/live logic here eg:
                context.Migrate();
                if(development)
                    context.Seed();
            }                
        }

        private static void Migrate(this GatewayDbContext context)
        {
            context.Database.EnsureCreated();
            if (context.Database.GetPendingMigrations().Any())
                context.Database.Migrate();
        }

        private static void Seed(this GatewayDbContext context)
        {
            context.AddOrUpdateSeedData();
            context.SaveChanges();
        }
Run Code Online (Sandbox Code Playgroud)

添加或更新种子数据.cs

internal static GatewayDbContext AddOrUpdateSeedData(this GatewayDbContext dbContext)
        {
            var defaultBand = dbContext.Bands
                .FirstOrDefault(c => c.Id == Guid.Parse("e96bf6d6-3c62-41a9-8ecf-1bd23af931c9"));

            if (defaultBand == null)
            {
                defaultBand = new Band { ... };
                dbContext.Add(defaultBand);
            }
            return dbContext;
        }
Run Code Online (Sandbox Code Playgroud)

  • 微软[文档](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli#apply-migrations-at-runtime)说“不要” t 在 Migrate() 之前调用 EnsureCreated()。EnsureCreated() 会绕过迁移来创建架构,这会导致 Migrate() 失败。” 你注意到了吗? (2认同)

Sha*_*med 7

就这样

 public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            try
            {
                SeedDatabase.Initialize(services);
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occured seeding the DB");
            }
        }
        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args)
    {
        var hb = Host.CreateDefaultBuilder(args)
             .ConfigureWebHostDefaults(webBuilder =>
             {
                 webBuilder.UseStartup<Startup>();
             });
        return hb;
    }
   
       
}
Run Code Online (Sandbox Code Playgroud)

种子数据库类:

  public static class SeedDatabase
{
    public static void Initialize(IServiceProvider serviceProvider)
    {
        using (var context = new HospitalManagementDbContext(serviceProvider.GetRequiredService<DbContextOptions<HospitalManagementDbContext>>()))
        {
            if (context.InvestigationTags.Any())
            {
                return;
            }

            context.InvestigationTags.AddRange(
                new Models.InvestigationTag
                {
                    Abbreviation = "A1A",
                    Name = "Alpha-1 Antitrypsin"
                },

                new Models.InvestigationTag
                {
                    Abbreviation = "A1c",
                    Name = "Hemoglobin A1c"
                },


                new Models.InvestigationTag
                {
                    Abbreviation = "Alk Phos",
                    Name = "Alkaline Phosphatase"
                }
                );
            context.SaveChanges();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

如果您想在应用程序启动时播种,在您的应用程序启动方法中,您可以使用条件检查检查所需的数据,如果没有返回,则将这些类添加到上下文中并保存更改。

EF Core 中的播种是为迁移而设计的,它为数据库初始化数据,而不是为应用程序运行时。如果您希望一组数据保持不变,那么请考虑使用替代方法?就像通过带有字段检查的属性将其保存在 xml/json 格式并在内存中缓存一样。

您可以在应用程序启动时使用删除/创建语法,但由于状态缺乏永久性,它通常不受欢迎。

不幸的是,对于您想要的东西,它必须是一种解决方法,因为它不在 EF 的预期功能范围内。


Yos*_*oss 5

您可以使用迁移来实现它。只需创建一个新的迁移(无需事先更改模型类)。生成的迁移类将具有空的 Up() 和 Down() 方法。在那里您可以进行播种。喜欢:

protected override void Up(MigrationBuilder migrationBuilder)
{
  migrationBuilder.Sql("your sql statement here...");
}
Run Code Online (Sandbox Code Playgroud)

就是这样。