Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core 2 Seed Database [duplicate]

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?

like image 257
forcequitIO Avatar asked Sep 14 '17 15:09

forcequitIO


2 Answers

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://learn.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();
}
like image 91
L. Piotrowski Avatar answered Nov 04 '22 10:11

L. Piotrowski


Update From Original Answer:

For .NET Core 2.0, Check out this answer instead

Original Answer:

I'm no .NET Core expert either, but this might be your solution.

In DBInitializer.cs

    public static void Seed(IApplicationBuilder applicationBuilder)
    {
        using (var serviceScope = applicationBuilder.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
                .CreateScope())
        {
            AppDbContext context = serviceScope.ServiceProvider.GetService<AppDbContext>();

            if (!context.Products.Any())
            {
                // Seed Here
            }

            context.SaveChanges();
        }
    }

The error suggests that the context should be scoped.

Also, If you haven't done so already, I'd take a look at the Introduction to Dependency Injection in ASP.NET Core doc, but more specifically, the Service Lifetimes and Registration Options section.

like image 8
Emmanuel Avatar answered Nov 04 '22 10:11

Emmanuel