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

6

29

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.

Dismiss answered 4/1, 2022 at 16:14 Comment(5)
i believe its part of a code migration.Elfish
well of course it is, but still I don't know how to do it and I can't find an answer anywhereDismiss
learn.microsoft.com/en-us/ef/core/modeling/data-seedingElfish
keep in mind that old EF had an AddOrUpdate() extension, but in ef core it's just Update(). #62449578Zandrazandt
What do you want to build? Razor pages or MVC. Razor learn.microsoft.com/en-us/aspnet/core/data/ef-rp/… MVC learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/…Adamina
S
21

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();
        }
    }
Start answered 7/2, 2022 at 7:39 Comment(1)
Why would I need the CreateScope()?Job
H
22

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

Hereupon answered 4/1, 2022 at 17:38 Comment(4)
With this approach, I'm finding that the normalized columns are not being populated, even if I explicitly set a value. Is there any way around that, so these columns get populated? (e.g. NormalizedEmail)Geminius
@Geminius I don't really understand your issue but I think it's unrelated. First, you have to make sure the data structure is completely synchronized from the code to the database tables with the migration commands. Second, to see how the migration works, you can run the script command learn.microsoft.com/en-us/ef/core/cli/…Stokeontrent
You're totally right, this was an unrelated issue - my apologies. I hadnt created a new migration after updating my code to set the normalized values. Thanks for your suggestions anyway.Geminius
I feel like maybe there should be a try/catch in here somewhere?Macymad
S
21

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();
        }
    }
Start answered 7/2, 2022 at 7:39 Comment(1)
Why would I need the CreateScope()?Job
B
18

.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();
    }
}
Beyer answered 26/5, 2022 at 9:9 Comment(3)
Repeats two other answers. Also, this is not the only seeding method. It should be understood in the light of the entire tutorial and it should be explained when this method is chosen and not other methods.Bazan
Considering this answer seems the most complete, and easiest to follow, I give them my vote. Plus, the question doesn't ask for an explanation comparing all possible solutions, so why is this the only answer that seems to have that as a requirement when the other answers give even less info?Vessel
I wish I could downvote comments. This answer is not a repeat of two other answers - it combines aspects of two other answers and adds useful refactors like implementation as an extension method and only seeding in Dev. As such it's not only the best answer, but arguably the only complete answer. Very few, if any, answers on this platform adhere to the standards requested by Gert's comment.Augustaaugustan
N
4

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);
}
Natica answered 20/4, 2022 at 4:28 Comment(1)
Doesnt this mean that your application will attempt to add the seed data every time it starts? I guess your DefaultRoles and DefaultAdmin classes can take care of idempotency checks, but it seems wrong to do this every time the application starts. It should probably be OnModelCreating in the context class.Geminius
T
4

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.

Themselves answered 26/10, 2022 at 23:59 Comment(0)
Q
0

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.

Quentin answered 31/12, 2023 at 12:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.