Asp.Net 核心 - 如何播种数据 - 未将对象引用设置为对象的实例

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

在将我的所有表转换为在标识列中开始使用 Guid 类型后,我无法播种数据,因此我简化了很多代码来定位错误,并以播种 class 结束,如下所示:

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()
    {
    }
}

这个class是从另一个调用的:

public interface IDatabaseInitializer
{
    Task SeedAsync();
}

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

从 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>();
            ...
            ...
     }

下面是它是如何从 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();
}

不幸的是,这个实现没有在数据库中植入任何数据,我能找到的唯一错误是在日志文件中,它说:

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

那我做错了什么?

new SeedTest() 不初始化其 _context 字段。您可以在 DatabaseInitializer 上使用 DI 来实例化 SeedTestMyDbContext

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();
    }
}

您在 DatabaseInitialize 中显式新建了一个 SeedTest 实例,而 DatabaseInitialize 实例正在由依赖项注入服务创建。在具有正确范围的服务中注册 SeedTest class 并让依赖项注入完成它的工作。

在 ConfigureServices 中添加类似

的东西
services.AddTransient<SeedTest>();

修改DatabaseInitializer

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

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

删除无参数的 SeedTest 构造函数并确保注册的 MyDbContext 类型是在另一个构造函数中传递的类型,因为您同时拥有 MyDbContext 和 DbContext。

你可以试试这个,我在这个示例中使用了 .net core 2.2 -

MyDbContext.cs

public class MyDbContext : DbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
        {
            Database.EnsureCreated();
        }

        public DbSet<Values> Values { get; set; }
    }

SeedTest.cs

public class SeedTest
    {
        private readonly MyDbContext _context;

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

        public async Task SeedTest1()
        {
            Values value1 = new Values
            {
                Id = Guid.Parse("AFE1052A-A694-48AF-AA77-56D2D945DE31"),
                ValueName = "value 1",
                Created = DateTime.Now
            };

            _context.Values.Add(value1);

            var value  = await _context.SaveChangesAsync();
        }

        public SeedTest()
        {
        }
    }

服务

public interface IDatabaseInitializer
    {
        Task SeedAsync();
    }

    public class DatabaseInitializer : IDatabaseInitializer
    {
        private readonly MyDbContext _cotext;

       // Inject DbContext 
        public DatabaseInitializer(MyDbContext dbContext)
        {
            _cotext = dbContext;
        }
        public async Task SeedAsync()
        {
            // Object with contructor which having DbContext parameter
            SeedTest _seedTest = new SeedTest(_cotext);
            await _seedTest.SeedTest1();
        }
    }

startup.cs

 services.AddTransient<IDatabaseInitializer, DatabaseInitializer>();
 services.AddDbContext<MyDbContext>(option=> option.UseSqlServer("Data Source=localhost;Initial Catalog=Whosebug1;Integrated Security=True"));

program.cs

public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateWebHostBuilder(args).Build();

            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var databaseInitializer = services.GetRequiredService<IDatabaseInitializer>();
                    databaseInitializer.SeedAsync().Wait();
                }
                catch (Exception ex)
                {
                }
            }
            host.Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }

您可以阅读有关 seed data 的更多信息。