Seed Data in Entity Framework Core

Seed Data in Entity Framework Core (EF Core)

In this article, I will discuss Seed Data in Entity Framework Core (EF Core) with Examples. Please read our previous article discussing Transactions in Entity Framework Core (EF Core) with Examples.

What Do You Mean by Seed Data in Entity Framework Core?

Seeding data in Entity Framework Core refers to the process of populating a database with initial data during the creation or migration of a database. This is particularly useful for setting up a known data state for testing, development, or deploying an application for the first time. Seed data can be useful for populating a database with a default data set during development or after deployment, such as initial roles, administrative user accounts, or master data. 

Example to Understand How to Seed Data in EF Core:

Let us understand how to seed data into database tables using Entity Framework Core with an example.

Define Your Model:

Before seeding data, first, we need to define the entity classes that make up our model. So, create the Country, City, and State entities that will hold the country, state, and city master data.

using System.ComponentModel.DataAnnotations.Schema;
namespace EFCoreCodeFirstDemo.Entities
{
    [Table("CountryMaster")]
    public class Country
    {
        public int CountryId { get; set; }
        public string CountryName { get; set; }
        public string CountryCode { get; set; }
    }

    [Table("StateMaster")]
    public class State
    {
        public int StateId { get; set; }
        public string StateName { get; set; }
        public int CountryId { get; set; }
    }

    [Table("CityMaster")]
    public class City
    {
        public int CityId { get; set; }
        public string CityName { get; set; }
        public int StateId { get; set; }
    }
}
Add Seed Data to Your DbContext:

Inside your DbContext class, override the OnModelCreating method to specify the seed data for your entities. You can specify seed data in the OnModelCreating method of your DbContext class using the HasData method of the entity configuration. So, modify the Context class as follows:

using Microsoft.EntityFrameworkCore;
namespace EFCoreCodeFirstDemo.Entities
{
    public class EFCoreDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //Configuring the Connection String
            optionsBuilder.UseSqlServer(@"Server=LAPTOP-6P5NK25R\SQLSERVER2022DEV;Database=EFCoreDB;Trusted_Connection=True;TrustServerCertificate=True;");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //Seeding Country Master Data using HasData method
            modelBuilder.Entity<Country>().HasData(
               new() { CountryId = 1, CountryName = "INDIA", CountryCode = "IND" },
               new() { CountryId = 2, CountryName = "Austrailla", CountryCode = "AUS" }
           );

            //Seeding State Master Data using HasData method
            modelBuilder.Entity<State>().HasData(
                new() {StateId =1, StateName = "ODISHA", CountryId = 1 },
                new() {StateId =2, StateName = "DELHI", CountryId = 1 }
            );

            //Seeding City Master Data using HasData method
            modelBuilder.Entity<City>().HasData(
                new() {CityId =1, CityName = "Bhubaneswar", StateId = 1 },
                new() {CityId =2, CityName = "Cuttack", StateId = 1 }
           );
        }

        public DbSet<Country> CountryMaster { get; set; }
        public DbSet<State> StateMaster { get; set; }
        public DbSet<City> CityMaster { get; set; }
    }
}
Create Migrations and Update the Database:

Once you have defined your seed data, you need to add a new migration that includes these changes. Then, apply the migration to your database to insert the seed data. This can be done by running the Update-Database command in the Package Manager Console, as shown below:

Create Migrations and Update the Database

Remember, seed data is inserted into the database only if the table is empty. If you add new seed data and want to reapply all seeds, you must ensure the tables are in the expected state or handle it programmatically.

Verify the Seed Data:

To verify the Seed Data, modify the Program class as follows:

using EFCoreCodeFirstDemo.Entities;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System.Data;

namespace EFCoreCodeFirstDemo
{
    public class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                using (EFCoreDbContext context = new EFCoreDbContext())
                {
                    Console.WriteLine("Country Master:");
                    var Countries = context.CountryMaster.ToList();
                    foreach (var country in Countries)
                    {
                        Console.WriteLine($"\tCountry ID: {country.CountryId}, Name: {country.CountryName}, Code: {country.CountryCode}");
                    }
                    Console.WriteLine("State Master:");
                    var States = context.StateMaster.ToList();
                    foreach (var state in States)
                    {
                        Console.WriteLine($"\tState ID: {state.StateId}, Name: {state.StateName}");
                    }
                    Console.WriteLine("City Master:");
                    var Cities = context.CityMaster.ToList();
                    foreach (var city in Cities)
                    {
                        Console.WriteLine($"\tCity ID: {city.CityId}, Name: {city.CityName}");
                    }
                }
                Console.Read();
            }

            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}"); ;
            }
        }
    }
}

Now run the application, and you should get the following output.

Seed Data in Entity Framework Core (EF Core) with Examples

Considerations When Seeding Data in EF Core
  • Idempotence: The seeding process is designed to be idempotent, which means it won’t try inserting seed data if it already exists in the database. This is achieved by checking the primary key values.
  • Limitations: Seed data is best used for relatively static data that doesn’t change often. It’s not suited for data that needs to be updated regularly.
  • Data Size: While there’s no strict limit on the amount of seed data, very large data sets might be better served by a custom data import process outside of EF Core migrations for performance reasons.
  • Data Changes: If you need to change seed data after it has been added to the database, you should create a new migration. Changing the HasData method and applying a new migration will adjust the seeded data accordingly.
Custom Initialization for Seed Data in EF Core

In Entity Framework Core, seeding data typically involves specifying static data directly within the OnModelCreating method of your DbContext using the HasData method. However, in some scenarios, you may need more custom and dynamic data initialization, which HasData does not directly support. For such cases, you can implement custom logic to seed data in your application startup routine or as part of the EF Core migration process.

Custom initialization in Entity Framework Core allows you more control over the seeding process, particularly when you have complex logic or large amounts of data to insert. This typically involves executing code to explicitly insert data into the database rather than using the HasData method, part of the model configuration.

Define the Seed Data Logic:

Create a method that contains the logic for seeding your database. This could be inside a static class or part of your DbContext. So, create a static class named DbInitializer (you can give any name) and then copy and paste the following code.

namespace EFCoreCodeFirstDemo.Entities
{
    public static class DbInitializer
    {
        public static void Initialize(EFCoreDbContext context)
        {
            context.Database.EnsureCreated();

            // Check if there are any Country or State or City already in the database
            if (context.CountryMaster.Any() || context.StateMaster.Any() || context.CityMaster.Any())
            {
                // Database has been seeded
                return;
            }
            else
            {
                //Here, you need to Implement the Custom Logic to Seed the Master data
                IList<Country> countries = new List<Country>();
                Country IND = new() { CountryName = "INDIA", CountryCode = "IND" };
                Country AUS = new() { CountryName = "Austrailla", CountryCode = "AUS" };
                countries.Add(IND);
                countries.Add(AUS);
                context.CountryMaster.AddRange(countries);

                IList<State> states = new List<State>();
                State Odisha = new() { StateName = "ODISHA", CountryId = IND.CountryId };
                State Delhi = new() { StateName = "DELHI", CountryId = IND.CountryId };
                states.Add(Odisha);
                states.Add(Delhi);
                context.StateMaster.AddRange(states);

                IList<City> cities = new List<City>();
                City BBSR = new() { CityName = "Bhubaneswar", StateId = Odisha.StateId };
                City CTC = new() { CityName = "Cuttack", StateId = Odisha.StateId };
                cities.Add(BBSR);
                cities.Add(CTC);
                context.CityMaster.AddRange(cities);
            }

            context.SaveChanges();
        }
    }
}
Modify the Context Class:

Next, modify the context class as follows:

using Microsoft.EntityFrameworkCore;
namespace EFCoreCodeFirstDemo.Entities
{
    public class EFCoreDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //Configuring the Connection String
            optionsBuilder.UseSqlServer(@"Server=LAPTOP-6P5NK25R\SQLSERVER2022DEV;Database=EFCoreDB;Trusted_Connection=True;TrustServerCertificate=True;");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
        }

        public DbSet<Country> CountryMaster { get; set; }
        public DbSet<State> StateMaster { get; set; }
        public DbSet<City> CityMaster { get; set; }
    }
}

Before proceeding further, let us delete the database and Migration folder and create the Migration file again to update the database, as shown in the image below.

Custom Initialization for Seed Data in EF Core

Call the Seed Method:

Now, you need to call the seed method at the start of your application. This could be done in the Main method, Startup class, or wherever it makes sense within your application’s lifecycle. In our example, let us call this from the Main method of the Program class:

using EFCoreCodeFirstDemo.Entities;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System.Data;

namespace EFCoreCodeFirstDemo
{
    public class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                using (EFCoreDbContext context = new EFCoreDbContext())
                {
                    //Call the Initialize Method to Seed the Data
                    DbInitializer.Initialize(context);

                    Console.WriteLine("Country Master:");
                    var Countries = context.CountryMaster.ToList();
                    foreach (var country in Countries)
                    {
                        Console.WriteLine($"\tCountry ID: {country.CountryId}, Name: {country.CountryName}, Code: {country.CountryCode}");
                    }
                    Console.WriteLine("State Master:");
                    var States = context.StateMaster.ToList();
                    foreach (var state in States)
                    {
                        Console.WriteLine($"\tState ID: {state.StateId}, Name: {state.StateName}");
                    }
                    Console.WriteLine("City Master:");
                    var Cities = context.CityMaster.ToList();
                    foreach (var city in Cities)
                    {
                        Console.WriteLine($"\tCity ID: {city.CityId}, Name: {city.CityName}");
                    }
                }
                Console.Read();
            }

            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}"); ;
            }
        }
    }
}
Output:

Seed Data in Entity Framework Core (EF Core)

Handling Changes: If you update your seed method, you must handle those changes manually, as they are not part of the EF Core migrations. You might need to write additional logic to check what data is already present and what needs to be updated, inserted, or deleted.

Considerations for Production: When deploying to production, you should be careful about when and how you run your seed method. You wouldn’t want to overwrite or duplicate data accidentally. Ensure that your seeding logic checks for existing data and only inserts where appropriate.

Using Transactions: For larger seeding operations or when data integrity is critical, you might use database transactions to ensure all seed data is committed or rolled back as a single unit.

namespace EFCoreCodeFirstDemo.Entities
{
    public static class DbInitializer
    {
        public static void Initialize(EFCoreDbContext context)
        {
            using (var transaction = context.Database.BeginTransaction())
            {
                try
                {
                    context.Database.EnsureCreated();

                    // Check if there are any Country or State or City already in the database
                    if (context.CountryMaster.Any() || context.StateMaster.Any() || context.CityMaster.Any())
                    {
                        // Database has been seeded
                        return;
                    }
                    else
                    {
                        //Here, you need to Implement the Custom Logic to Seed the Master data
                        IList<Country> countries = new List<Country>();
                        Country IND = new() { CountryName = "INDIA", CountryCode = "IND" };
                        Country AUS = new() { CountryName = "Austrailla", CountryCode = "AUS" };
                        countries.Add(IND);
                        countries.Add(AUS);
                        context.CountryMaster.AddRange(countries);

                        IList<State> states = new List<State>();
                        State Odisha = new() { StateName = "ODISHA", CountryId = IND.CountryId };
                        State Delhi = new() { StateName = "DELHI", CountryId = IND.CountryId };
                        states.Add(Odisha);
                        states.Add(Delhi);
                        context.StateMaster.AddRange(states);

                        IList<City> cities = new List<City>();
                        City BBSR = new() { CityName = "Bhubaneswar", StateId = Odisha.StateId };
                        City CTC = new() { CityName = "Cuttack", StateId = Odisha.StateId };
                        cities.Add(BBSR);
                        cities.Add(CTC);
                        context.CityMaster.AddRange(cities);
                    }

                    context.SaveChanges();
                    transaction.Commit();
                }
                catch (Exception ex)
                {
                    transaction.Rollback();
                    // Handle or log the exception
                }
            }   
        }
    }
}
Considerations for Custom Seeding in EF Core:
  • Idempotency: Ensure that your seeding logic is idempotent. It should be safe to run multiple times without causing duplicate data or other issues.
  • Data Size: If seeding large amounts of data, consider performance implications. Large data inserts can be resource-intensive.
  • Data Complexity: Custom seeding methods can handle complex data relationships or dynamic data more effectively than HasData.
  • Environment Specific Data: You might want to seed different data based on the environment (development, staging, production). Custom seeding methods can provide the flexibility to achieve this.

In the next article, I will discuss Shadow Properties in Entity Framework Core with Examples. In this article, I try to explain Seed Data in Entity Framework Core (EF Core) with Examples. I hope you enjoy this Seed Data in EF Core article.

Leave a Reply

Your email address will not be published. Required fields are marked *