web-dev-qa-db-ja.com

Entity Framework Core 2にシードする方法は?

2つのテーブルがあり、シードを使用してそれを埋めたいです。

UbuntuでASP.NET Core 2を使用しています。

外部キーによって一方が他方に接続されている2つのテーブルのデータを設定するにはどうすればよいですか?流量計には多くのメモがあり、メモはフロメータに属します。このようなことをしたいのですが、データベースに保存する必要があります。

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}
    }
}
35
zshanabek

Entity Framework Core 2.1現在、データをシードする新しい方法があります。 DbContextクラスオーバーライドOnModelCreatingで:

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

また、関連するエンティティについては、匿名クラスを使用して、関連するエンティティの外部キーを指定します。

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

重要:OnModelCreatingメソッドとUpdate-Databaseにこのデータを入力してデータを更新した後、追加移行を実行する必要があることに注意してください。

公式ドキュメントは 更新済み です。

52
Blake Mumford

これは、EF Core 2.0のソリューションであり、 https://docs.Microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#move-database -初期化コード

Program.csで

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

....

それから私の種まきクラス

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;
    }
}
13
Labi

tl; drdwCheckApiプロジェクト を見て、どのように実装したかを確認してください。

他の人が言ったように、JSONなどからシードデータを読み取ることができます(必要に応じてソース管理できます)。

私のプロジェクトで実装した方法は、StartupクラスのConfigureメソッドで呼び出されるメソッドを持つことです(開発中の場合のみ):

if (env.IsDevelopment())
{
  app.EnsureDatabaseIsSeeded(false);
}

以下を呼び出します:

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

私の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;
}

このアプリケーションは、本、キャラクター、シリーズの関係を示すことを目的としています。 3人の播種者がいるのはそのためです。

そして、これらのシーダーメソッドの1つは次のようになります。

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

おそらく素晴らしいコードではないかもしれませんが、跳ね返る出発点になるかもしれません。

シーダーは開発環境でのみ呼び出されるため、アプリケーションがそのように起動することを確認する必要があります(コマンドラインから起動する場合は、ASPNETCORE_ENVIRONMENT=Development dotnet runを使用して開発で起動することを確認できます)。

また、本番環境でデータベースをシードするための異なるアプローチが必要になることも意味します。 dwCheckApiには、データベースをシードするために呼び出すことができるコントローラーがあります( DatabaseControllerのSeedDataメソッド を見て、その方法を確認してください)。

6
Jamie Taylor

この方法で移行をクリーンに保つことができないため、およびDbContext内のOnModelCreating()が少し間違っていると感じるデータに依存し始めるため、Microsoftのドキュメントに書かれているよりもHasDataアプローチが好きではありませんランダムデータジェネレーターの問題。

私にとって最も効率的で快適な方法は、このようなDbSetごとにシードクラスを作成することです。 (偽のライブラリを使用すると、呼吸と同じくらい簡単です)

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

次に、開発環境でのみ機能するサービスコントローラーを作成します。

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

そして、いつでもInsomnia\Postmanから呼び出します。

4
Liam Kernighan

Jsonでシードを作成し、Asp.netコアStartupをバッチで追加します

https://garywoodfine.com/how-to-seed-your-ef-core-database/ に非常に似ています

すぐに使えるソリューションはまだ見つかりませんでした。

0
Calin

私は同じ質問に出くわし、次の方法でシードを修正しました。

まず、モデルに garywoodfinepublic static bool AllMigrationsApplied(this DbContext context)を追加しました。

次に、dbをシードするサービススコープを実装しました-> see this blog

次に、 このブログ のチュートリアルに従って、NBuilderとFakerを使用してテストデータを生成するコードでpublic static void EnsureSeedDataを作成しました

これが、プロジェクトの自動テストシードの実装に役立つことを願っています。現在、私は自分でこれを実装するのに忙しく、時間があれば、これを行う方法に関するコードサンプルを投稿します。

0
botenvouwer

このトピックにまだ興味がある人のために、データシードのプロセスを簡素化する一連のツール(.netコアグローバルツールとライブラリ)を作成しました。

簡単に言えば、現在のDBのコンテンツをいくつかのJSONまたはXMLファイルに保存し、それらのファイルをロードしてそこに保存されたデータをDBにインポートするコードをアプリに追加できます。ツールセットは完全に無料で、 オープンソース です。

詳細なステップバイステップの手順は ここに公開されています です。

0
Sergiy