EntityFrameworkCore Базы данных SQLite в памяти не создаются - PullRequest
0 голосов
/ 27 мая 2019

Для интеграционных тестов я использую EntityFrameworkCore SQLite в памяти и создаю его схему в соответствии с документацией 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 Ответ

1 голос
/ 29 мая 2019

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

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

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

EF Core 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...