JerZaw
JerZaw

Reputation: 615

How to seed data in .NET Core 6 with Entity Framework?

I know how to seed data to a database with old .NET 5.0 in startup.cs file using my Seeder class with a Seed() method creating some initial data.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, Seeder seeder)
{
   seeder.Seed();

   ..............
   // other configurations
}

How do I do this in .NET 6.0? There is no place to add my Seeder class as an argument.

Upvotes: 31

Views: 56609

Answers (6)

M.Youssef
M.Youssef

Reputation: 148

I know the question about .NET 6.0 but I think it's in the same concepts.

In my case, I'm using .NET 8.0, and using Microsoft.AspNetCore.Identity so I need to initialize the application with default values (Defulat User and Defulat Roles) in the database using seed methods.

So First I create a class and inside it, I define an enum "Contains the roles I have" and write the main info related to the default user "Name, Email, Password, Role...etc".

Then I create a class called ApplicationDbContextSeed :

public class ApplicationDbContextSeed
    {
        public async Task<bool> SeedEssentialsAsync(UserManager<ApplicationUser> userManager,
                                                    RoleManager<IdentityRole> roleManager)
        {
            //Seed Roles
            await roleManager.CreateAsync(new IdentityRole(Authorization.Roles.SuperUser.ToString()));
            await roleManager.CreateAsync(new IdentityRole(Authorization.Roles.User.ToString())); 
    
             
    
            //Seed Default User
            var defaultUser = new ApplicationUser
            {
                Firstname= Authorization.default_Firstname,
                Lastrname= Authorization.default_Lastname,
                UserName = Authorization.default_username,
                Email = Authorization.default_email,
                EmailConfirmed = true,
                PhoneNumberConfirmed = true,
                IsPasswordChanged = true, 
                IsActive = true
            };
            if (userManager.Users.All(u => u.Id != defaultUser.Id))
            {
                await userManager.CreateAsync(defaultUser, Authorization.default_password);
                await userManager.AddToRoleAsync(defaultUser, Authorization.superuser_role.ToString());
            }
    
            return true;
        }
    }

In Program.cs :

var app = builder.Build();
if (args.Length == 1 && args[0].ToLower() == "seeddata")
 await SeedData(app);

async Task SeedData(IHost app)
{
    var scopedFactory = app.Services.GetService<IServiceScopeFactory>();
    using (var scope = scopedFactory?.CreateScope())
    {
        var service = scope?.ServiceProvider.GetService<ApplicationDbContextSeed>();
        var userManager = scope?.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
        var roleManager = scope?.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
        await service.SeedEssentialsAsync(userManager, roleManager);
    }
}

After I created my first migration Add-Migration InitialCreate and created the database using the statement Update-database, the tables were empty, and I filled it with the initial data using the statement dotnet run seeddata

Hope it helps anybody stuck in this part.

Upvotes: 0

RChamy
RChamy

Reputation: 61

Seeder.cs

public static class Seeder
{
public static void Initialize(DatabaseContext context)
{
    context.Database.EnsureCreated();
    //your seeding data here
    context.SaveChanges();

}
}

Program.cs

var app = builder.Build();
SeedDatabase();

void SeedDatabase()
    using(var scope = app.Services.CreateScope())
    try{
        var scopedContext = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
        Seeder.Initialize(scopedContext);
        }
catch{
    throw;
}

as simple as it gets before using DI.

Upvotes: 5

phougatv
phougatv

Reputation: 1004

.NET 6.0

Use these links for detailed version:

Program.cs

var builder = WebApplication.CreateBuilder(args);

//Add services to the container.
builder.Services.AddDbContext<YourDbContext>(
    optionsBuilder => optionsBuilder.UseSqlServer("Your connection string goes here") //install - Microsoft.EntityFrameworkCore.SqlServer to use ".UseSqlServer" extension method
builder.Services.AddScoped<DbInitializer>();

var app = builder.Build();

//Configure the HTTP-request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseItToSeedSqlServer();    //custom extension method to seed the DB
    //configure other services
}

app.Run();

DbInitializerExtension.cs

internal static class DbInitializerExtension
{
    public static IApplicationBuilder UseItToSeedSqlServer(this IApplicationBuilder app)
    {
        ArgumentNullException.ThrowIfNull(app, nameof(app));

        using var scope = app.ApplicationServices.CreateScope();
        var services = scope.ServiceProvider;
        try
        {
            var context = services.GetRequiredService<YourDbContext>();
            DbInitializer.Initialize(context);
        }
        catch (Exception ex)
        {

        }

        return app;
    }
}

DbInitializer.cs

internal class DbInitializer
{
    internal static void Initialize(YourDbContext dbContext)
    {
        ArgumentNullException.ThrowIfNull(dbContext, nameof(dbContext));
        dbContext.Database.EnsureCreated();
        if (dbContext.Users.Any()) return;

        var users = new User[]
        {
            new User{ Id = 1, Name = "Bruce Wayne" }
            //add other users
        };

        foreach(var user in users)
            dbContext.Users.Add(user);

        dbContext.SaveChanges();
    }
}

Upvotes: 18

Rafael Francisco
Rafael Francisco

Reputation: 126

In my case, I'm using Microsoft.AspNetCore.Identity and needed initialize application with default values in database using seed methods.

builder.Services.AddScoped<UserManager<ApplicationUser>>();
builder.Services.AddScoped<RoleManager<IdentityRole>>();

And after line containing

var app = builder.Build();

I have called the seeds methods:

using (var scope = app.Services.CreateScope())
{
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<Usuario>>();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
await DefaultRoles.SeedAsync(roleManager);
await DefaultAdmin.SeedAsync(userManager);
}

Upvotes: 5

Virtual
Virtual

Reputation: 234

I solved a similar problem as follows:

Program.cs (.NET 6)

...
builder.Services.AddScoped<IDbInitializer, DbInitializer>(); //can be placed among other "AddScoped" - above: var app = builder.Build();   

...    
SeedDatabase(); //can be placed above app.UseStaticFiles();
...    
    void SeedDatabase() //can be placed at the very bottom under app.Run()
    {
        using (var scope = app.Services.CreateScope())
        {
            var dbInitializer = scope.ServiceProvider.GetRequiredService<IDbInitializer>();
            dbInitializer.Initialize();
        }
    }

Upvotes: 21

V&#245; Quang H&#242;a
V&#245; Quang H&#242;a

Reputation: 3025

I have never use your solution before. This is what I'm doing,

public class DataContext: DbContext
{
    public DataContext(DbContextOptions options) : base(options)
    {
        
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        new DbInitializer(modelBuilder).Seed();
    }
    
    // Db sets
}

And

public class DbInitializer
{
    private readonly ModelBuilder modelBuilder;

    public DbInitializer(ModelBuilder modelBuilder)
    {
        this.modelBuilder = modelBuilder;
    }

    public void Seed()
    {
        modelBuilder.Entity<User>().HasData(
               new User(){ Id = 1.... },
               new User(){ Id = 2.... },

        );
    }
}

Then run the command

dotnet ef migrations add .......

To create migration file

And

dotnet ef database update

To update db

Upvotes: 23

Related Questions