【问题标题】:How to access open DbContext without explicit reference?如何在没有显式引用的情况下访问打开的 DbContext?
【发布时间】:2015-03-16 17:46:41
【问题描述】:

我已经使用实体框架有一段时间了,我遇到过几个场景,其中两个上下文会尝试访问同一个实体等,所以我想知道我是否没有打开/以最好的方式关闭我的 dbcontexts。

目前,我基本上按照最初设置基本 MVC 应用程序的方式在每个控制器上打开一个 DbContext,这意味着我在控制器上有一个私有 dbcontext 字段,并且我重写了控制器的 dispose 方法以在上下文中调用 dispose .

不过,我有时也会在我的其他一些类中对数据库进行查询,这些类可以从控制器内部调用,控制器也有一个打开的上下文。

有没有办法在没有显式处理程序的情况下访问开放上下文?通过十几种不同的方法传递 DbContext 引用对我来说真的没有意义。

【问题讨论】:

  • 打开、使用和关闭它。最好使用 using 结构。
  • @Ako 虽然这是我对 ADO.NET SqlConnection 连接的建议,但 EF 确实在内部做了一些特殊的事情(特别是缓存实体),这些事情有一个长期存在的 DbContext 可以承受多个数据调用可以带来性能优势。
  • 你检查过this 的帖子吗?我想它可能会回答你的问题。

标签: c# asp.net asp.net-mvc entity-framework


【解决方案1】:

使用依赖注入

正如其他人所说并且可能会重申的那样,“正确的方式”通常被认为是依赖注入。

在我最近的项目中,我已经组织了一些事情,所以我几乎完成了这个项目,而 DI 非常轻松,我自己做(而不是使用注射器)。其中一个主要因素是相当严格地遵守这种结构:

                           WebProject
                             |    |
                             |   DataServices
                             |    |        | 
                           ViewModels    EntityModels

在一个工作单元期间对所有数据服务的访问通过单个 DataServiceFactory 实例发生,这需要一个 MyDbContext 实例。另一个因素是完全 RESTful 的应用程序设计 - 这意味着我不必在我的代码中散布持久性功能。

没有依赖注入

也就是说,也许 DI 在这个项目中不适合你。也许:

  • 您不打算编写单元测试
  • 您需要更多时间来了解 DI
  • 您的项目结构已经深度集成了 EF

在 ASP.NET MVC 中,工作单元通常与请求生命周期完全一致 - 即 HttpContext.Current。因此,您可以根据请求懒惰地实例化存储库“单例”,而不是使用 DI。这是一个以当前上下文作为后备存储的经典单例模式,用于保存您的DbContext

public class RepositoryProxy {
    private static HttpContext Ctx { get { return HttpContext.Current; } }
    private static Guid repoGuid = typeof(MyDbContext).GUID;

    public static MyDbContext Context {
        get {
            MyDbContext repo = Ctx.Items[repoGuid];
            if (repo == null) {
                repo = new MyDbContext();
                Ctx.Items[repoGuid] = result;
            }
            return repo;
        }
    }

    public static void SaveIfContext() {
        MyDbContext repo = Ctx.Items[repoGuid];
        if (repo != null) repo.SaveChanges();
    }
}

如果你觉得特别懒惰,你也可以自动SaveChanges(当然,你仍然需要手动调用它来检查副作用,比如检索新项目的 id):

public abstract class ExtendedController : Controller {
    protected MyDbContext Context {
        get { return RepositoryProxy.Context; }
    }

    protected override void OnActionExecuted(ActionExecutedContext filterContext) {
        RepositoryProxy.SaveIfContext();
        base.OnActionExecuted(filterContext);
    }
}

【讨论】:

  • 感谢您的详细回答。我仍在为依赖注入的概念而苦苦挣扎。关于您的第三个“可能”点,您的意思是实体框架与依赖注入方法完全分开,即互斥?我想我会试试你的单例方法,看看效果如何。
  • 不客气。不,实体框架非常适合依赖注入。但是,您提到您正在“通过十几种不同的方法传递 [ing] DbContext 引用”。听起来您的项目目前非常广泛地直接依赖于持久性。来自一些非常深的依赖关系。例如,您可能直接从 ViewModel 调用 EF。或者来自各种业务逻辑。 DI 并没有真正解决这个问题,它只是让处理起来更加痛苦,所以你不太可能这样做:D
  • 好的,我已经实现了单例,它已经干净多了。我知道 DI 有利于单元测试,因为接口使用可以让您轻松地模拟上下文,但是这种方法还有其他好处吗?似乎我读到的每篇文章都有不同的看法。
  • 这是一个棘手的问题。 IoC 的好处来自于遵循的抽象。您可以注入的不仅仅是模拟。您可以更换组件或将其重新用于其他目的。但这通常需要已经非常熟悉 IoC 的人的设计意图。 DI 框架将减少在 IoC 设计中插入依赖项的工作量。一开始对你有什么好处?除了学习之外可能真的什么都没有,但这会让你做好充分利用它的准备。
  • 好吧,我想我开始感觉到它的用处了。这似乎是用范式解释最难的部分。一半的网站说“这样做是因为它遵循 DI 范式”,而不是“这是 DI 范式,因为它允许我们 X”。再次感谢您的帮助!
【解决方案2】:

处理DbContext 实例的“最佳”方式是将其作为每个需要它的方法的参数(或者,如果整个类都需要它,则作为构造函数的参数)。

这是 IoC(控制反转)的基本部分,它允许调用者指定方法的依赖关系,从而允许调用者“控制”被调用方法的行为。

然后您也可以添加依赖注入框架。它们可以配置为使用您的 DbContext 的单例实例,并将该实例注入到任何需要它的方法中。

【讨论】:

    【解决方案3】:

    我建议你传递某种工厂类的实例,而不是传递 DbContext 的实例,这将为你创建一个 DbContext 的实例。

    在大多数情况下,您只需创建 DbContext 将实现的接口,如下所示:

    public interface IDbContext
    {
        IDbSet<TEntity> Set<TEntity>() where TEntity : class;
        DbSet Set(Type entityType);
        DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
        int SaveChanges();
    }
    

    然后是工厂返回您的实例:

    public interface IContextFactory
    {
        IDbContext Retrieve();
    }
    

    可以轻松地模拟工厂以返回您想要的任何实现 - 这是一个很好的方法,特别是如果您打算测试应用程序。您的 DbContext 将仅派生自 IDbContext 接口并实现必要的方法和属性。这种方法假设,要告诉 EF 你的数据库表你使用OnModelCreating(DbModelBuilder modelBuilder) 方法和modelBuilder.Configurations.Add(new EntityTypeConfiguratinOfSomeType());,而不是在你的上下文中使用DbSet&lt;T&gt; 类型的属性。

    要从 Entity 框架中完全抽象出来,您可以创建另一个抽象层,例如使用 UnitOfWork 或 Repository 模式方法,但我想前者暂时涵盖了您的担忧。

    【讨论】:

      猜你喜欢
      • 2010-11-23
      • 2013-08-07
      • 2010-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多