如何在Entity Framework Core 2中播种?

zsh*_*bek 39 c# entity-framework-core .net-core

我有两张桌子,我想用种子填充它.

我在Ubuntu中使用ASP.NET Core 2.

如何填充两个表的数据,哪一个与外键连接?流量计有许多注释,注意属于flometer.我想做这样的事情,但它应该存储在数据库中.

new Flowmeter 
{
    Make = "Simple model name",
    SerialNum = 45, 
    Model = "Lor Avon", 
    Notes = new List<Note>()
    {
        new Note() { Value = 45, CheckedAt = System.DateTime.Now },
        new Note() { Value = 98, CheckedAt = System.DateTime.Now }
    }
}
Run Code Online (Sandbox Code Playgroud)

Bla*_*ord 55

实体框架核心2.1开始,现在有一种新的种子数据播种方法.在你的DbContext班级覆盖中OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Url = "http://sample.com" });
}
Run Code Online (Sandbox Code Playgroud)

对于相关实体,使用匿名类并指定相关实体的外键:

modelBuilder.Entity<Post>().HasData(
    new {BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1"},
    new {BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2"});
Run Code Online (Sandbox Code Playgroud)

请注意,在OnModelCreating方法和Update-Database中输入此数据后,您需要运行添加迁移以更新数据.

官方文档已更新.

  • 请在您的`OnModelCreating`方法和Update-Database中输入此数据后,将其添加到您需要运行ADD-MIGRATION的注释中以更新数据! (13认同)
  • 你会怎么做有条件的种子?我们需要在树中播种根节点,但是只有第一次创建数据库时才会生成,目前我们有一个类和一个方法来检查数据是否存在然后不播种. (4认同)
  • @Matt数据播种现在是迁移步骤。只要您正确使用了迁移,数据就不会再被播种!这是我们与EF6相比必须做不同的主要原因之一。 (2认同)

Lab*_*abi 13

这是我的EF Core 2.0解决方案,改编自https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#move-database-initialization-code

在program.cs中

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Seed().Run();
    }
Run Code Online (Sandbox Code Playgroud)

....

然后我的播种机课

public static class DatabaseSeedInitializer
{
    public static IWebHost Seed(this IWebHost host)
    {
        using (var scope = host.Services.CreateScope())
        {
            var serviceProvider = scope.ServiceProvider;

            try
            {
                Task.Run(async () =>
                {
                    var dataseed = new DataInitializer();
                    await dataseed.InitializeDataAsync(serviceProvider);
                }).Wait();

            }
            catch (Exception ex)
            {
                var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred seeding the DB.");
            }
        }
        return host;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 记录后重新抛出错误是不是有意义? (2认同)

Jam*_*lor 7

tl; dr:看看我的dwCheckApi项目,看看我是如何实现它的.

正如其他人所说,你可以从JSON或类似的东西中读取你的种子数据(如果你愿意的话,它可以是源代码控制的).

我在我的项目中实现它的方法是Configure在Startup类中的方法中调用一个方法(仅在开发时):

if (env.IsDevelopment())
{
  app.EnsureDatabaseIsSeeded(false);
}
Run Code Online (Sandbox Code Playgroud)

它调用以下内容:

public static int EnsureDatabaseIsSeeded(this IApplicationBuilder applicationBuilder,
 bool autoMigrateDatabase)
{
    // seed the database using an extension method
    using (var serviceScope = applicationBuilder.ApplicationServices
   .GetRequiredService<IServiceScopeFactory>().CreateScope())
   {
       var context = serviceScope.ServiceProvider.GetService<DwContext>();
       if (autoMigrateDatabase)
       {
           context.Database.Migrate();
       }
       return context.EnsureSeedData();
   }
}
Run Code Online (Sandbox Code Playgroud)

我的DbContext类型DwContext是扩展EF Core DbContext类型的类

EnsureSeedData扩展方法如下:

public static int EnsureSeedData(this DwContext context)
{
    var bookCount = default(int);
    var characterCount = default(int);
    var bookSeriesCount = default(int);

    // Because each of the following seed method needs to do a save
    // (the data they're importing is relational), we need to call
    // SaveAsync within each method.
    // So let's keep tabs on the counts as they come back

    var dbSeeder = new DatabaseSeeder(context);
    if (!context.Books.Any())
    {
        var pathToSeedData = Path.Combine(Directory.GetCurrentDirectory(), "SeedData", "BookSeedData.json");
        bookCount = dbSeeder.SeedBookEntitiesFromJson(pathToSeedData).Result;
    }
    if (!context.BookCharacters.Any())
    {
        characterCount = dbSeeder.SeedBookCharacterEntriesFromJson().Result;
    }
    if (!context.BookSeries.Any())
    {
        bookSeriesCount = dbSeeder.SeedBookSeriesEntriesFromJson().Result;
    }

    return bookCount + characterCount + bookSeriesCount;
}
Run Code Online (Sandbox Code Playgroud)

此应用程序旨在显示书籍,字符和系列之间的关系.这就是为什么有三个播种机.

其中一种播种方法如下所示:

public async Task<int> SeedBookEntitiesFromJson(string filePath)
{
    if (string.IsNullOrWhiteSpace(filePath))
    {
        throw new ArgumentException($"Value of {filePath} must be supplied to {nameof(SeedBookEntitiesFromJson)}");
    }
    if (!File.Exists(filePath))
    {
        throw new ArgumentException($"The file { filePath} does not exist");
    }
    var dataSet = File.ReadAllText(filePath);
    var seedData = JsonConvert.DeserializeObject<List<Book>>(dataSet);

    // ensure that we only get the distinct books (based on their name)
    var distinctSeedData = seedData.GroupBy(b => b.BookName).Select(b => b.First());

    _context.Books.AddRange(distinctSeedData);
    return await _context.SaveChangesAsync();
}
Run Code Online (Sandbox Code Playgroud)

这里可能有一些代码不是很好,但它可能是你反弹的起点.

因为仅在开发环境中调用了播种器,所以您需要确保应用程序以这种方式启动(如果从命令行开始,您可以使用ASPNETCORE_ENVIRONMENT=Development dotnet run它来确保它在开发中启动).

这也意味着您需要一种不同的方法来为生产中的数据库播种.在dwCheckApi中,我有一个可以调用种子数据库的控制器(看一下DatabaseController的SeedData方法,看看我是怎么做的).


Lia*_*han 7

我不喜欢 Microsoft 文档中编写的 HasData 方法,因为我无法以这种方式保持迁移干净,并且因为OnModelCreating()在我DbContext开始时依赖于感觉有点错误的数据并导致随机数据生成器出现问题。

对我来说,最有效和最舒适的方法是为我的每个 DbSet 创建一个种子类,如下所示。(有了 Bogus 库,就像呼吸一样简单)

using Bogus;

        // namespace, class, etc.


        // CategorySeeder seed method
        public int Seed(AppDbContext context)
        {


            var faker = new Faker<Category>()
                .RuleFor(r => r.IsGroup, () => true)
                .RuleFor(r => r.Parent, () => null)
                .RuleFor(r => r.UniversalTimeTicks, () => DateTime.Now.ToUniversalTime().Ticks)
                .RuleFor(r => r.Title, f => "Folder: " + f.Random.Word());

            var folders1 = faker.Generate(5);

            faker.RuleFor(r => r.Parent, () => folders1.OrderBy(r => Guid.NewGuid()).First());
            var folders2 = faker.Generate(10);
            var folders3 = folders1.Concat(folders2).ToArray();

            faker.RuleFor(r => r.Parent, () => folders3.OrderBy(r => Guid.NewGuid()).First());
            faker.RuleFor(r => r.Title, f => f.Random.Word());
            faker.RuleFor(r => r.IsGroup, () => false);

            var elements = faker.Generate(20);

            var allSeeds = elements.Concat(folders3).ToArray();

            context.AddRange(allSeeds);
            context.SaveChanges();
            return allSeeds.Length;
        }

        // ProductSeeder Seed method
        public int Seed(AppDbContext context)
        {
            var faker = new Faker<Product>()
                .RuleFor(r => r.Sku, f => f.Random.AlphaNumeric(8))
                .RuleFor(r => r.Title, f => f.Random.Word())
                .RuleFor(r => r.Category, () => context.Categories.Where(c => !c.IsGroup).OrderBy(o => Guid.NewGuid()).First());

            var prod = faker.Generate(50);
            context.AddRange(prod);
            context.SaveChanges();
            return prod.Count;
        }
Run Code Online (Sandbox Code Playgroud)

然后创建仅在开发环境中工作的服务控制器。

    public class DataGeneratorController : BaseController
    {
        public DataGeneratorController(IServiceProvider sp) : base(sp) { }

        public IActionResult SeedData()
        {
            var lst = new List<string>();

            if (!_dbContext.Categories.Any())
            {
                var count = new CategoryConfiguration().Seed(_dbContext);
                lst.Add($"{count} Categories have been seeded.");
            }

            if (!_dbContext.Products.Any())
            {
                var count = new ProductConfiguration().Seed(_dbContext);
                lst.Add($"{count} Products have been seeded.");
            }

            if (lst.Count == 0)
            {
                lst.Add("Nothing has been seeded.");
            }

            return Json(lst);
        }
    }
Run Code Online (Sandbox Code Playgroud)

并随时从 Insomnia\Postman 调用它。


Cal*_*lin 0

我用 json 创建了种子,然后将它们批量添加到我的 Asp.net core Startup

https://garywoodfine.com/how-to-seed-your-ef-core-database/非常相似

尚未找到开箱即用的解决方案。