简体   繁体   中英

Garbage collection for Entity Framework dbcontext c#

just wondering if I dispose my dbcontext object correctly here or should I be using the using block instead?

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

    }        
}

I read that I should not be calling dispose manually from

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

But in some sample code, I also notice this scaffolding code but not too clear how it does the job on its own.

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

DbContexts are designed to be short-lived. The very first initialization and use of a DbContext presents a spin up cost to resolve the entity mappings, but aside from that the context can be scoped to individual calls, or sets of calls. Your code will work fine and so long as your repo is disposed, the dbContext will be cleaned up. There are pitfalls with this approach though in that as the product matures it is easy to forget to dispose something, and these DbContexts can soak up a fair bit of memory if they are long-lived.

To avoid issues with entities that become disconnected from their DbContext, an entity should never leave the scope of it's DbContext. If it does, you run into errors if a lazy load gets triggered for example.

For instance lets say I have a method in a Controller or such that does something like this: (Note: I don't advocate ever returning Entities to a view, but for example's sake...)

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

The repo will be disposed, and if bank account either has no references, or all references are eager loaded, this call would work just fine. However, if there is a lazy load call, the controller method will fail because the DbContext associated with the Bank Account was disposed.

This can be compensated for by ensuring the return occurs inside the scope of the using block:

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

To help avoid issues like this, it is generally a better idea to create POCO view model classes to populate within the scope of the DbContext from the entities, then return those view models. No surprise lazy load hits etc.

Where this really starts to crumble apart is when you want to coordinate things like updates across entities to ensure that updates are committed or rolled back together. Each of your repo classes are going to have separate DbContext instances.

The first default approach to get familiar with to address this is Dependency Injection and Inversion of Control, particularly an IoC container such as Autofac , Unity, Ninject, or Castle Windsor. Using these, you can have your repository classes accept a dependency on a DbContext, and they can scope a single instance of a Dependency across a lifetime. (such as per HTTP Request for example) In this way, the references of all of your repositories in a single session call will be provided the same DbContext instance. A call to SaveChanges() will attempt to commit all pending changes.

A better pattern is the Unit of Work pattern where the scope of the DbContext is moved outside of the repository and each repository is either provided a reference to the DbContext, or can locate it. (similar to how the IoC pattern works) The advantage of UoW patterns is that you can move control of the commit/rollback out to the consumer of the repositories I promote the use of Mehdime's DbContextScope since it negates the need to pass around references to the UoW/DbContext. Mehdime DbContextScope (EF6 original github ) EFCore supported Port

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM