ASP.NET Core RC2 Seed Database

2019-01-25 08:39发布

问题:

My problem is i am trying to seed an Entity Framework Core database with data and in my mind the below code show work. I've realised that this should not be called in the ApplicationDbContext constructor and should be called from the startup but im not sure how to do this.

EDIT: Based on the solution provided by Ketrex, my solution is as follows:

Startup.cs:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        ... 

        app.ApplicationServices.GetRequiredService<ApplicationDbContext>().Seed();
    }

Seed extension:

public static class DbContextExtensions
{
    public static void Seed(this ApplicationDbContext context)
    {
        // Perform database delete and create
        context.Database.EnsureDeleted();
        context.Database.EnsureCreated();

        // Perform seed operations
        AddCountries(context);
        AddAreas(context);
        AddGrades(context);
        AddCrags(context);
        AddClimbs(context);

        // Save changes and release resources
        context.SaveChanges();
        context.Dispose();
    }

    private static void AddCountries(ApplicationDbContext context)
    {
        context.AddRange(
            new Country { Name = "England", Code = "En" },
            new Country { Name = "France", Code = "Fr" }
            );
    }

    ...
}

I understand that seeding a database is quite high up on the priority list for Entity Framework but it would be great if there was some documentation on how to achieve this trivial task or at least provide a temporary work around. If someone can provide some guidance on how to do this it would be greatly appreciated. I feel i'm close to a solution but just cant piece it together.

Thanks for any help.

回答1:

Assuming you are using the built-in DI container, here is one way you can accomplish this.

Reference your seed method in the Configure method of your startup class, and pass the IApplicationBuilder object as a parameter instead of the DbContext, like this:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //...
    // Put this at the end of your configure method
    DbContextSeedData.Seed(app);
}

Next, modify your seed method to accept the IApplicationBuilder instance. Then you'll be able to spin up an instance of the DbContext, and perform your seed operation, like this:

public static void Seed(IApplicationBuilder app)
{
    // Get an instance of the DbContext from the DI container
    using (var context = app.ApplicationServices.GetRequiredService<ApplicationDbContext>())
    {
        // perform database delete
        context.Database.EnsureDeleted;
        //... perform other seed operations
    }
}


回答2:

You could also use from the Startup.cs ConfigureServices method to make your ApplicationDbContext available (Registering the dbcontext as a service):

public void ConfigureServices(IServiceCollection services)
{
   var connectionString = Startup.Configuration["connectionStrings:DBConnectionString"];//this line is not that relevant, the most important thing is registering the DbContext
            services.AddDbContext<ApplicationDbContext>(o => o.UseSqlServer(connectionString));
}

and then add your ApplicationDbContext as dependency in your Configure method which will call your seed extension method.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, ApplicationDbContext myApplicationDbContext)
{
    //...
    myApplicationDbContext.Seed();
}

Finally the seed method could do a quick check on an important table, because perhaps recreating the db is too heavy:

public void Seed()
{
 //....      
 if(context.Countries.Any())
   return;
 //...
}

I hope it helps you or someone else, at least as another option.



回答3:

If you want to run your EF code from a separate class library and do Seeding in that you can do the following. This is using TSQL...

1) Create a new class library. Add the following dependencies with NuGet...

Microsoft.AspNetCore
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools

2) Point the Package Manager Console at this project and run...

PM> add-migration Seeder01

then...

PM> update-database

this gives you an empty migration.

3) Script the updates as something like...

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using System.IO;

namespace Test02.Data.Migrations
{
    public partial class Seeder01 : Migration
    {

        protected override void Up(MigrationBuilder migrationBuilder)
        {
            string sql = string.Empty;

            sql = "SET IDENTITY_INSERT State ON;";
            sql += "Insert into State (Id, Name) values ";
            sql += "(2, 'NSW'),";
            sql += "(3, 'VIC'),";
            sql += "(4, 'QLD'),";
            sql += "(5, 'SA')";
            sql += ";";
            sql += "SET IDENTITY_INSERT State OFF;";
            migrationBuilder.Sql(sql);

        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            string sql = string.Empty;
            sql = "delete State;";
            migrationBuilder.Sql(sql);


        }
    }
}

4) Revert to the prior migration with...

PM> add-migration {PriorMigrationName}

Reload the seed migration and update the database...

PM> add-migration Seeder01
PM> update-database