54 votes

Comment semer dans Entity Framework Core 2?

J'ai deux tables et je veux le remplir avec des graines.

J'utilise ASP.NET Core 2 dans Ubuntu.

Comment renseigner les données de deux tables dont l'une est connectée à une autre par clé étrangère? Le débitmètre a beaucoup de notes et la note appartient au flomètre. Je veux faire quelque chose comme ça, mais cela devrait être stocké dans une base de données.

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

72voto

Angavar Points 378

Comme d' Entity Framework Core 2.1 il existe maintenant une nouvelle méthode de semis de données. Dans votre DbContext classe de remplacer OnModelCreating:

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

Et pour les entités liées, l'utilisation anonyme de classes et de spécifier la clé étrangère de l'entité correspondante:

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

Veuillez noter que vous devrez exécuter un complément de migration après la saisie de ces données dans votre OnModelCreating méthode et mise à Jour de la Base de données pour mettre à jour vos données.

L'officiel docs ont été mis à jour.

15voto

Labi Points 211

C’est ma solution pour EF Core 2.0, adaptée de https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#move-database-initialization-code.

Dans program.cs

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

....

Puis ma classe de semoir

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

11voto

Jamie Taylor Points 782

tl;dr: un coup d'oeil par le biais de mon dwCheckApi projet pour voir comment j'ai mis en œuvre.

Comme d'autres l'ont dit, vous pouvez lire vos données semences de JSON ou similaires (de cette façon, il peut être source contrôlée, si vous voulez).

Le chemin que j'ai mis en place dans le cadre de mes projets est d'avoir une méthode qui est appelée dans l' Configure méthode dans le Démarrage de classe (uniquement en développement):

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

qui appelle suivantes:

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

Mon DbContext est de type DwContext qui est une classe qui étend le EF de Base DbContext type

L' EnsureSeedData extension de la méthode ressemble à ceci:

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

Cette application est destinée à montrer les liens entre les livres, les personnages et les séries. C'est pourquoi il y a trois semoirs.

Et l'une de ces semoir méthodes ressemble à ceci:

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

Il y a probablement un peu de code ici qui n'est pas grand, mais il pourrait être un point de départ pour vous de rebondir sur de.

Parce que les semoirs sont seulement appelés lorsque dans l'environnement de développement, vous aurez besoin de vous assurer que votre application commence de cette façon (si le démarrage à partir de la ligne de commande vous pouvez utiliser ASPNETCORE_ENVIRONMENT=Development dotnet run pour s'assurer qu'il démarre en développement).

Cela signifie également que vous aurez besoin d'une approche différente à l'ensemencement de votre base de données en production. Dans dwCheckApi, j'ai un contrôleur qui peut être appelé pour les semences de la base de données (jetez un oeil à la DatabaseController de SeedData méthode pour voir comment je le faire).

7voto

Liam Kernighan Points 391

Je n'aime pas le HasData approche que celle qui a été écrit dans la documentation de Microsoft parce que je ne peux pas garder mon migrations propre de cette façon, & parce que l' OnModelCreating() mon DbContext commence à dépendre des données qui se sent un peu mal et provoque des problèmes avec le générateur de données aléatoires.

Pour moi, la plus efficace et la plus confortable est de créer une semence de classe pour chacun de mes DbSets qui ressemble à ceci. (Avec de Faux bibliothèque c'est aussi facile que de respirer)

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

Puis créer le contrôleur de service, qui ne fonctionne que dans un environnement de développement.

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

Et l'appeler à partir d'Insomnie\Postier quand je veux.

0voto

Calin Points 1217

J'ai créé mes graines en json, et je les ai ajoutées par lot à mon coeur Asp.net Startup

Très similaire à https://garywoodfine.com/how-to-seed-your-ef-core-database/

Vous n'avez pas encore trouvé de solution prête à l'emploi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X