Сборка мусора для Entity Framework dbcontext c#

#c# #entity-framework #garbage-collection #entity-framework-6

#c# #entity-framework #сборка мусора #entity-framework-6

Вопрос:

просто интересно, правильно ли я размещаю здесь свой объект dbcontext или вместо этого мне следует использовать блок using?

 public class RepoBankAccount : IBankAccount
{
    private AppDbContext db = null;

    public RepoBankAccount()
    {
        this.db = new AppDbContext();
    }

    public RepoBankAccount(AppDbContext db)
    {
        this.db = db;
    }

    public IEnumerable<BankAccount> ViewAllBankAccount()
    {
        return db.BankAccounts.ToList();   
    }

    public BankAccount ViewBankAccount(long accountNumber)
    {
        return db.BankAccounts.Where(b => b.AccountNumber.Equals(accountNumber)).SingleOrDefault();            
    }


    public void DeleteBankAccount(BankAccount bankAccount)
    {
        db.BankAccounts.Remove(bankAccount);
        Save();
    }

    public void InsertBankAccount(BankAccount bankAccount)
    {
        db.BankAccounts.Add(bankAccount);
        Save();
    }        

    public void Save()
    {
        try
        {
            db.SaveChanges();
        }
        catch(Exception ex)
        {
            System.Console.WriteLine("Error:"   ex.Message);
        }
        finally
        {
            if(db != null)
            db.Dispose();
        }

    }        
}
  

Я читал, что я не должен вызывать dispose вручную из

https://softwareengineering.stackexchange.com/questions/359667/is-it-ok-to-create-an-entity-framework-datacontext-object-and-dispose-it-in-a-us

Но в некоторых примерах кода я также замечаю этот код каркаса, но не слишком понятно, как он выполняет работу сам по себе.

     protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            db.Dispose();
        }
        base.Dispose(disposing);
    }
  

Ответ №1:

DbContexts спроектированы так, чтобы быть недолговечными. Самая первая инициализация и использование DbContext сопряжены с дополнительными затратами на разрешение сопоставлений сущностей, но помимо этого контекст может быть ограничен отдельными вызовами или наборами вызовов. Ваш код будет работать нормально, и до тех пор, пока ваш репозиторий будет удален, DbContext будет очищен. Однако в этом подходе есть подводные камни, поскольку по мере созревания продукта легко забыть что-либо удалить, и эти DbContexts могут занимать изрядный объем памяти, если они долговечны.

Чтобы избежать проблем с объектами, которые отключаются от своего DbContext, объект никогда не должен покидать область своего DbContext. Если это произойдет, вы столкнетесь с ошибками, например, при отложенной загрузке.

Например, допустим, у меня есть метод в контроллере или что-то подобное, который делает что-то вроде этого: (Примечание: я не сторонник когда-либо возвращать объекты в представление, но ради примера …)

 public ActionResult View(long accountNumber)
{
   BankAccount bankAccount;
   using (var repo = new RepoBankAccount())
   {
      bankAccount = repo.ViewBankAccount(accountNumber);
   }
   return new View(bankAccount);
}
  

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

Это можно компенсировать, гарантируя, что возврат происходит внутри области блока using:

 public ActionResult View(long accountNumber)
{
   using (var repo = new RepoBankAccount())
   {
      BankAccount bankAccount = repo.ViewBankAccount(accountNumber);
      return new View(bankAccount);
   }
}
  

Чтобы избежать подобных проблем, обычно лучше создавать классы моделей представления POCO для заполнения в пределах области DbContext из сущностей, а затем возвращать эти модели представления. Неудивительно, что задержка загрузки и т.д.

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

Первый подход по умолчанию, с которым следует ознакомиться для решения этой проблемы, — это внедрение зависимостей и инверсия управления, в частности, в контейнерах IoC, таких как Autofac, Unity, Ninject или Castle Windsor. Используя их, вы можете заставить классы вашего репозитория принимать зависимость от DbContext, и они могут охватывать один экземпляр зависимости в течение всего срока службы. (например, по HTTP-запросу) Таким образом, ссылки на все ваши репозитории в одном сеансовом вызове будут предоставлены одним и тем же экземпляром DbContext. Вызов SaveChanges() попытается зафиксировать все ожидающие изменения.

Лучшим шаблоном является шаблон Unit of Work, в котором область действия DbContext перемещается за пределы репозитория, и каждому репозиторию либо предоставляется ссылка на DbContext, либо он может его найти. (аналогично тому, как работает шаблон IoC) Преимущество шаблонов UoW заключается в том, что вы можете передать управление фиксацией / откатом потребителю репозиториев. Я пропагандирую использование DbContextScope от Mehdime, поскольку это сводит на нет необходимость передавать ссылки на UoW / DbContext. Интерфейс DbContextScope (оригинал EF6 на github), поддерживаемый EFCore порт

Комментарии:

1. Большое спасибо Стиву Пи. Я вроде понимаю, что вы пытаетесь сказать в целом, и некоторые концепции, мне все еще нужно немного времени, чтобы попытаться «переварить» это. Мне нужно будет больше прочитать о внедрении зависимостей и IoC.