Asp.Net Core - How to seed data - Object reference not set to an instance of an object

asp.net-core c# entity-framework-core seeding

Question

After converted all my tables to start using Guid type in identity columns, I failed to seed data, so I simplified a lot the code to localize the error, and ended with a seeding class as follows:

public class SeedTest
{
    private readonly MyDbContext _context;

    public SeedTest(MyDbContext context)
    {
        _context = context;
    }

    public async Task SeedTest()
    {
        Values value1 = new Values
        {
            Id = Guid.Parse("29c48913-1b5c-47b8-g144-08d6d2273deb"),
            ValueName = "value 1",
            Created = DateTime.Now
        };

        _context.Values.Add(value1);

        await _context.SaveChangesAsync();
    }

    public SeedTest()
    {
    }
}

This class is called from another one:

public interface IDatabaseInitializer
{
    Task SeedAsync();
}

public class DatabaseInitializer : IDatabaseInitializer
{
    public async Task SeedAsync()
    {
            SeedTest _seedTest = new SeedTest();
            await _seedTest.SeedTest();
    }
}

which is called from startup.cs

    public class Startup
    {
        public IConfiguration Configuration { get; }
        private readonly IHostingEnvironment _hostingEnvironment;

        public Startup(IConfiguration configuration, IHostingEnvironment env)
        {
            Configuration = configuration;
            _hostingEnvironment = env;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            ...
            services.AddMyDbContext<MyDbContext>(options =>
                options.UseSqlServer("ConnectionStrings:MyCn"));
            ...
            // DB Seeding
            services.AddTransient<IDatabaseInitializer, DatabaseInitializer>();
            ...
            ...
     }

And here is how it is triggered from program.cs

public class Program
{

    public static void Main(string[] args)
    {
        var host = BuildWebHost(args);

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            try
            {
                var databaseInitializer = services.GetRequiredService<IDatabaseInitializer>();
                databaseInitializer.SeedAsync().Wait();
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogCritical(LoggingEvents.INIT_DATABASE, ex, LoggingEvents.INIT_DATABASE.Name);
            }
        }

        host.Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                    .UseStartup<Startup>()
                    .Build();
}

Unfortunately this implementation didn't seed any data in the database, the unique error I could find is in the logs files, and it says:

System.NullReferenceException: Object reference not set to an instance of an object. and it points to the last line of SeedTest class.

So what am I doing wrong ?

1
0
6/20/2019 12:15:25 AM

Accepted Answer

new SeedTest() does not initialize its _context field. You could use DI on your DatabaseInitializer to instantiate a SeedTest with a MyDbContext.

public class DatabaseInitializer : IDatabaseInitializer
{
    private readonly MyDbContext _context;

    public DatabaseInitializer(MyDbContext context)
    {
        _context = context;
    }

    public async Task SeedAsync()
    {
        SeedTest _seedTest = new SeedTest(_context);
        await _seedTest.SeedTest();
    }
}
3
6/20/2019 12:07:38 AM

Popular Answer

You are explicitly newing an instance of SeedTest in DatabaseInitialize, while the instance of DatabaseInitialize is being created by the dependency injection service. Register the SeedTest class in the services with the correct scope and let the dependency injection do its thing.

In ConfigureServices add something like

services.AddTransient<SeedTest>();

Modify DatabaseInitializer

public class DatabaseInitializer : IDatabaseInitializer{
     private readonly SeedTest _seedTest;
     public DatabaseInitializer(SeedTest seedTest) 
     {
          _seedTest = seedTest;
     }

     public async Task SeedAsync()
     {
         await _seedTest.SeedTest();
     }
}

Remove the parameterless SeedTest constructor and make sure the MyDbContext type registered is what is passed in the other constructor as you have both MyDbContext and DbContext.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow