Search code examples
asp.netasp.net-coreseeding

Seeding data using static method and IApplicationBuilder in ASP.NET Core


I really don't understand what's going on in this EnsurePopulated method which creates seeding data.

public static class SeedData
{
    public static void EnsurePopulated(IApplicationBuilder app)
    {
        StoreDbContext context = app.ApplicationServices
        .CreateScope().ServiceProvider.GetRequiredService<StoreDbContext>();

        if (context.Database.GetPendingMigrations().Any())
        {
            context.Database.Migrate();
        }
        if (!context.Products.Any())
        {
            //here we add the products (I left just one in the code sample)
            context.Products.AddRange(
            new Product
            {
                Name = "Kayak",
                Description = "A boat for one person",
                Category = "Watersports",
                Price = 275
            });

            context.SaveChanges();
        }
    }
}

Here is the Program.cs file of my ASP.NET app.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

builder.Services.AddDbContext<StoreDbContext>(opts =>
{
    opts.UseSqlServer(builder.Configuration["ConnectionStrings:StortsStoreConnection"]);
});

builder.Services.AddScoped<IStoreRepository, EFStoreRepository>();

var app = builder.Build();

app.MapDefaultControllerRoute();

SeedData.EnsurePopulated(app);

app.Run();

What is the idea of the IApplicationBuilder argument and how exactly are we getting the db context? What do we use .ApplicationServices, .CreateScope(), .ServiceProvider, .GetRequiredService<StoreDbContext>() for? Can you explain what each one of these do? I don't see why can't we just inject the db context into the constructor of the class (if it weren't static). May you clarify for me the whole idea of seeding data this way? I tried to see what's happening with the debugger, but it wasn't very helpful. Please don't be rude as I am fairly new to the framework. Thanks!


Solution

  • We register service in IServiceCollection , the IServiceCollection interface is used for building a dependency injection container. Such as, the database context is registered with the Dependency Injection container in the Program.cs file:

    builder.Services.AddDbContext<StoreDbContext>(opts =>
    {
        opts.UseSqlServer(builder.Configuration["ConnectionStrings:StortsStoreConnection"]);
    });
    

    After it's fully built, it gets composed to an IServiceProvider instance which you can use to resolve services. Read Resolve a service at app start up to know more.

    You can inject an IServiceProvider into any class. The IApplicationBuilder class can provide the service provider as well, via ApplicationServices .

    By default, Entity Framework contexts are added to the service container using the scoped lifetime because web app database operations are normally scoped to the client request. Use .CreateScope() creates a new IServiceScope that can be used to resolve scoped services.

    ServiceProvider class implements IServiceProvider , use .GetRequiredService<StoreDbContext>() method to get service of type StoreDbContext from the IServiceProvider.

    StoreDbContext context = app.ApplicationServices .CreateScope().ServiceProvider.GetRequiredService();

    This is for resolving StoreDbContext, then use context.Products.AddRange and context.SaveChanges(); to seed data. You can read Seed the database to know more.