EntityFrameworkCore Базы данных SQLite в памяти не создаются

Для интеграционных тестов я использую EntityFrameworkCoreSQLite in-memory db и создание его схемы в соответствии с документами Microsoft, но когда я пытаюсь заполнить данные, возникает исключение, что таблицы не существуют.

Мышиные документы для DbContext.Database.EnsureCreated();:

Убедитесь, что база данных для контекста существует. Если он существует, никаких действий не предпринимается. Если он не существует, то создается база данных и вся ее схема. Если база данных существует, то не предпринимается никаких действий для обеспечения ее совместимости с моделью для этого контекста.

Я читал, что EntityFrameworkCore БД в памяти существует только до тех пор, пока существует открытое соединение, и поэтому я попытался явно создать var connection = new SqliteConnection("DataSource=:memory:"); экземпляр и оборачивая приведенный ниже код в using(connection) {} заблокировать и передать экземпляр подключения options.UseSqlite(connection);, но DbContext.Database.EnsureCreated(); до сих пор не создает DB-объектов

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<Startup>
{
    protected override IWebHostBuilder CreateWebHostBuilder()
    {
        return WebHost.CreateDefaultBuilder()
            .UseStartup<Startup>();
    }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
      using (var connection = new SqliteConnection("DataSource=MySharedInMemoryDb;mode=memory;cache=shared"))
      {
          connection.Open();
          builder.ConfigureServices(services =>
          {
              var serviceProvider = new ServiceCollection()
                  .AddEntityFrameworkSqlite()
                  .BuildServiceProvider();

              services.AddDbContext<MyDbContext>(options =>
              {
                  options.UseSqlite(connection);
                  options.UseInternalServiceProvider(serviceProvider);
              });

              var contextServiceProvider = services.BuildServiceProvider();

              // we need a scope to obtain a reference to the database contexts
              using (var scope = contextServiceProvider.CreateScope())
              {
                  var scopedProvider = scope.ServiceProvider;

                  var logger = scopedProvider.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

                  using (var myDb = scopedProvider.GetRequiredService<MyDbContext>())
                  {
                      // DEBUG CODE
                      // this returns script to create db objects as expected
                      // proving that MyDbContext is setup correctly
                      var script = myDb.Database.GenerateCreateScript();
                      // DEBUG CODE

                      // this does not create the db objects ( tables etc )
                      // this is not as expected and contrary to ms docs
                      var result = myDb.Database.EnsureCreated();

                      try
                      {
                          SeedData.PopulateTestData(myDb);
                      }
                      catch (Exception e)
                      {
                          // exception is thrown that tables don't exist
                          logger.LogError(e, $"SeedData.PopulateTestData(myDb) threw exception=[{e.Message}]");
                      }
                  }
              }
          });
        }
        builder.UseContentRoot(".");
        base.ConfigureWebHost(builder);
    }

Обратите внимание, что в этом посте я только задаю вопрос, почему не DbContext.Database.EnsureCreated(); создать схему, как и ожидалось. Я не представляю приведенный выше код в качестве общего шаблона для запуска интеграционных тестов.

1 ответ

Использование не разделяемой базы данных SQLite в памяти

Базы данных SQLite в памяти по умолчанию являются временными. Как указано в документации:

База данных перестает существовать, как только соединение с базой данных закрывается. Every:memory: база данных отличается от всех остальных.

EF Core's DbContext с другой стороны, всегда открывает и закрывает соединения с базой данных автоматически, если только вы не пропустили уже открытое соединение.

Следовательно, чтобы использовать одну и ту же базу данных SQLite в памяти для нескольких вызовов в EF Core, необходимо создать SqliteConnection объект отдельно, а затем передать его каждому DbContext,

Например:

  var keepAliveConnection = new SqliteConnection("DataSource=:memory:");
  keepAliveConnection.Open();

  services.AddDbContext<MyContext>(options =>
  {
    options.UseSqlite(keepAliveConnection);
  });

Обратите внимание, что SqliteConnection на самом деле не ориентирован на многопоточность, поэтому этот подход применим только к однопоточным сценариям. Каждый раз, когда вы хотите иметь общую базу данных, к которой могут обращаться несколько потоков (например, в приложении ASP.NET Core, обслуживающем несколько запросов), вам следует рассмотреть возможность использования базы данных на диске.

Кстати, этот подход в настоящее время используется в документации EF Core о том, как использовать базы данных SQLite в памяти для тестирования.

Использование общей базы данных SQLite в памяти

SQLite также поддерживает именованные общие базы данных в памяти. Используя одну и ту же строку подключения, несколько SqliteConnection объекты могут подключаться к одной базе данных. Тем не мение:

База данных автоматически удаляется, а память восстанавливается при закрытии последнего подключения к базе данных.

Поэтому по-прежнему необходимо поддерживать отдельный объект открытого соединения, чтобы база данных могла использоваться для нескольких вызовов EF Core. Например:

  var connectionString = "DataSource=myshareddb;mode=memory;cache=shared";
  var keepAliveConnection = new SqliteConnection(connectionString);
  keepAliveConnection.Open();

  services.AddDbContext<MyContext>(options =>
  {
    options.UseSqlite(connectionString);
  });

Обратите внимание, что этот подход не ограничен одним потоком, потому что каждый DbContext получает свой собственный экземпляр SqliteConnection,

Другие вопросы по тегам