首页 > 解决方案 > 实体框架 dbcontext c# 的垃圾收集

问题描述

只是想知道我是否在这里正确处理了我的 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);
    }

标签: c#entity-frameworkgarbage-collectionentity-framework-6

解决方案


DbContexts 被设计为短暂的。DbContext 的第一次初始化和使用提出了解决实体映射的加速成本,但除此之外,上下文可以限定为单个调用或一组调用。您的代码将正常工作,只要您的 repo 被处理,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 范围内,然后返回这些视图模型。毫不奇怪的延迟加载命中等。

当您想要跨实体协调更新等事情以确保更新被提交或回滚时,这真正开始崩溃的地方。您的每个 repo 类都将具有单独的 DbContext 实例。

熟悉解决此问题的第一个默认方法是依赖注入和控制反转,尤其是 IoC 容器,例如Autofac、Unity、Ninject 或 Castle Windsor。使用这些,您可以让您的存储库类接受对 DbContext 的依赖,并且它们可以在整个生命周期中限定一个 Dependency 实例。(例如每个 HTTP 请求)这样,单个会话调用中所有存储库的引用都将提供相同的 DbContext 实例。调用 SaveChanges() 将尝试提交所有挂起的更改。

更好的模式是工作单元模式,其中 DbContext 的范围被移到存储库之外,并且每个存储库都提供对 DbContext 的引用,或者可以找到它。(类似于 IoC 模式的工作方式)UoW 模式的优点是您可以将提交/回滚的控制权转移给存储库的使用者我提倡使用 Mehdime 的 DbContextScope,因为它不需要传递对UoW/DbContext。 Mehdime DbContextScope (EF6 original github ) EFCore 支持的端口


推荐阅读