Безопасно ли повторно использовать Entity Framework Sqlite DBContext? - PullRequest
0 голосов
/ 18 декабря 2018

Я делаю что-то вроде:

  public class MyDbContext : DbContext {
    public MyDbContext(bool readOnlyFlag) {
      // Monitor.Enter(theLock); // needed ??
      this.readOnlyFlag = readOnlyFlag; 
      // Database.EnsureCreated(); // needed ??
    }

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

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
      string connectionString = "Data Source=C:\\mydb.db;";
      if (readOnlyFlag) connectionString += "Mode=ReadOnly;";
      optionsBuilder.UseSqlite(connectionString);
    }

    public override void Dispose() {
      // Database.CloseConnection(); // needed ??
      base.Dispose();
      // Monitor.Exit(theLock); // needed ??
    }

    readonly bool readOnlyFlag;
    // readonly object theLock = new object(); // needed ??
  }

, а затем:

using (var dbc = new MyDbContext(true)) {
  dbc.MyData.Where( ... code
}

Я называю такой код из нескольких параллельных потоков для выполнения разных запросов .. (в .Net CoreКонсольное приложение 3.0)

Вопросы:

  1. Если я правильно понимаю, файл базы данных будет открыт, когда блок using начнется, и закроется, когда он закончится.Закрытие и открытие файла по каждому запросу кажется действительно неэффективным, но я не смог найти какой-либо ссылки на то, нормально ли сохранять одноэлементный MyDbContext (то есть в классе Program) и использовать его повторно?

  2. Если я могу повторно использовать MyDbContext, должен ли я тогда использовать блокировку запросов?

  3. В общем, нужно ли мне использовать, например, Monitor, отмеченный выше, чтобы убедиться, что запросы не выполняются одновременно?Я видел сообщения о том, что Sqlite нуждается в этом?

  4. Нужно ли звонить Database.CloseConnection()?Кажется, работает нормально без него, но я видел сообщения, где он был назван, как отмечено выше?

  5. это Database.EnsureCreated (), необходимый для Sqlite?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 18 декабря 2018

Вы уверены, что являетесь единственным пользователем данных?Другими словами, вы уверены, что данные не меняются между двумя использованиями вашего dbContext?

Более того: вы уверены, что ваш dbContext всегда будет использоваться таким образом, или это может быть в будущем этот dbContextможет быть подключен к реальной базе данных?

Если ваш поток будет единственным пользователем, сейчас и в будущем, то при повторном использовании DbContext не будет большого вреда.Однако имейте в виду, что не гарантируется, что данные действительно будут записаны до того, как вы утилизируете dbContext.Более того: ваш dbContext будет хранить все извлеченные данные в локальной памяти, поэтому через некоторое время у вас будет полная база данных в локальной памяти.

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

Например, если у вас естьбаза данных со школами, студентами и учителями, и вы часто запрашиваете их данные, но редко запрашиваете данные ушедших в отставку учителей и данные о выпускниках, ваш репозиторий может хранить в памяти все извлеченные не вышедшие на пенсию / законченные учителя / учащиеся и создавать только свежие данныеdbContext для извлечения неизвестных данных, извлечения данных об устаревших / оконченных программах или обновления базы данных

interface IRepositorySet<Tentity> : IEnumerable<Tentity>
     where Tentity : class, new()
{
     Tentity Add(Tentity entity);
     Tentity Update(Tentity entity);
     Tentity Delete(Tentity entity);
}
interface ISchoolsRepository
{
     // for simple queries / add / update / remove only
     IRepositorySet<School> Schools {get;}
     IRepositorySet<Teacher> Teachers {get;}
     IRepositorySet<Student> Students {get;}
}

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

При создании словарь заполняется всеми первичными ключами и значением null, что указывает на то, что элемент еще не выбран.

Когда запрашиваются данные, RepositorySet сначала выбирает данные из словаря.Все элементы, которые все еще имеют нулевое значение, будут извлечены из нового dbContext и помещены в словарь.

Обратите внимание, что это не будет работать для огромных объемов данных.Рассматривайте это решение только в том случае, если считаете, что можете хранить все извлеченные данные в памяти.Но опять же: сохранение открытого dbContext также сохранит все извлеченные данные в памяти.

0 голосов
/ 18 декабря 2018

Вы можете использовать DbContext с многопоточностью Sqlite.Обычно вы должны использовать DbContext в качестве экземпляра для каждого запроса, поскольку DbContext не является потокобезопасным, один коммит не должен влиять на другие.

Как упоминалось на сайте sqlite, он поддерживает многопоточность:

SQLite поддерживает три различных режима потоков:

Однопоточный.В этом режиме все мьютексы отключены, и SQLite небезопасно использовать более чем в одном потоке одновременно.

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

Сериализовано.В сериализованном режиме SQLite может безопасно использоваться несколькими потоками без ограничений.

Режим потоков может быть выбран во время компиляции (когда библиотека SQLite компилируется из исходного кода) или во время запуска (когда приложение, которое намеревается использовать SQLite, инициализируется) или во время выполнения (когда создается новое соединение с базой данных SQLite).Вообще говоря, время выполнения переопределяет время начала и время начала переопределяет время компиляции.За исключением того, что однопотоковый режим нельзя переопределить после выбора.

Режим по умолчанию сериализуется.

https://www.sqlite.org/threadsafe.html

Также я предлагаю вам взглянутьна этом параллельном доступе SQLite и на этом Могу ли я читать и записывать в базу данных SQLite одновременно из нескольких соединений? .

В соответствии с приведенными выше постами, sqlite write блокирует всюфайл даже для чтения.А в Интернете некоторые из пользователей предлагают явно блокировать код для записи.

Но в новой версии sqlite есть функция под названием WAL .

Вторым преимуществом WAL-режима является то, что писатели не блокируют читателей, а читатели не блокируют писателей.Это в основном правда.Но есть некоторые неясные случаи, когда запрос к базе данных в режиме WAL может вернуть SQLITE_BUSY, поэтому приложения должны быть готовы к этому случаю.

Сам Sqlite говорит, что параллельный доступ даже для нескольких процессов может обрабатываться sqlite.

И в соответствии с sqlite.org / faq

Если вашему приложению требуется много параллелизма, вам следует рассмотреть возможность использования клиента /база данных сервера.Но опыт показывает, что большинству приложений требуется гораздо меньше параллелизма, чем представляют их разработчики. Когда SQLite пытается получить доступ к файлу, заблокированному другим процессом, поведение по умолчанию - возвращать SQLITE_BUSY.

Может потребоваться обработка в самом приложении.

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