ASP.NET Core 2 Seed Database

forcequitIO picture forcequitIO · Sep 14, 2017 · Viewed 31.2k times · Source

I've seen some of the similar examples on SO regarding this but I don't know enough about the language just yet to see what I'm doing wrong. I've cobbled together a demo to learn more but I'm having trouble seeding my database.

I receive the following error:

InvalidOperationException: Cannot resolve scoped service 'demoApp.Models.AppDbContext' from root provider.

Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, ServiceProvider serviceProvider)

Here are the three files in question:

Models/AppDbContext.cs

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {

    }
    public DbSet<Product> Products{ get; set; }
    public DbSet<Category> Categories { get; set; }
}

Models/DBInitializer.cs

public static class DbInitializer
{
    public static void Seed(IApplicationBuilder applicationBuilder)
    {
        //I'm bombing here
        AppDbContext context = applicationBuilder.ApplicationServices.GetRequiredService<AppDbContext>();

        if (!context.Products.Any())
        {
            // Add range of products
        }

        context.SaveChanges();
    }

    private static Dictionary<string, Category> _categories;
    public static Dictionary<string, Category> Categories
    {
        get
        {
            if (_categories == null)
            {
               // Add categories...
            }

            return _categories;
        }
    }
}

Startup.cs

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<ICategoryRepository, CategoryRepository>();
    services.AddTransient<IProductRepository, ProductRepository>();

    services.AddDbContext<AppDbContext>(options => 
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddMvc();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
        app.UseStatusCodePages();
        
        // Kersplat!
        DbInitializer.Seed(app);
    }
    else ...
    
    app.UseStaticFiles();
    app.UseMvc(routes => {...});
}

Can someone help explain what I'm doing wrong and how to remedy the situation?

Answer

Lek Pio picture Lek Pio · Oct 11, 2017

In ASP.NET Core 2.0 the following changes are recommended. (Seeding in startup.cs works for Core 1.x. For 2.0 go into Program.cs, modify the Main method to do the following on application startup: Get a database context instance from the dependency injection container. Call the seed method, passing to it the context. Dispose the context when the seed method is done. (Here's a sample from the Microsoft site. https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro )

public static void Main(string[] args)
{
var host = BuildWebHost(args);

using (var scope = host.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    try
    {
        var context = services.GetRequiredService<yourDBContext>();
        DbInitializer.Seed(context);//<---Do your seeding here
    }
    catch (Exception ex)
    {
        var logger = services.GetRequiredService<ILogger<Program>>();
        logger.LogError(ex, "An error occurred while seeding the database.");
    }
}

host.Run();
}