Ядро Asp.Net - Как заполнять данные - Ссылка на объект не установлена ​​на экземпляр объекта - PullRequest
0 голосов
/ 20 июня 2019

После преобразования всех моих таблиц для начала использования типа Guid в столбцах идентификаторов мне не удалось заполнить данные, поэтому я значительно упростил код для локализации ошибки и закончил классом заполнения следующим образом:

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

Этот класс вызывается из другого:

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: Ссылка на объектне установлен для экземпляра объекта.и он указывает на последнюю строку класса SeedTest.

Так, что я делаю не так?

Ответы [ 3 ]

3 голосов
/ 20 июня 2019

new SeedTest() не инициализирует свое поле _context.Вы можете использовать DI на вашем DatabaseInitializer для создания экземпляра SeedTest с 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();
    }
}
1 голос
/ 20 июня 2019

Вы явно обновляете экземпляр SeedTest в DatabaseInitialize, в то время как экземпляр DatabaseInitialize создается службой внедрения зависимостей. Зарегистрируйте класс SeedTest в службах с правильной областью действия, и пусть внедрение зависимости сделает свое дело.

В 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.

0 голосов
/ 20 июня 2019

Вы можете попробовать это, я использовал .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=StackOverFlow1;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>();
    }

Вы можете узнать больше о начальных данных .

...