如何在没有显式引用的情况下访问打开的DbContext?

时间:2015-03-16 17:46:41

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

我现在一直在使用实体框架,我遇到过两个场景,两个上下文会尝试访问同一个实体等,所以我想知道是否我不打开/以最佳方式关闭我的dbcontexts。

目前我基本上在每个控制器上打开一个DbContext,就像最初设置基本MVC应用程序一样,这意味着我在控制器上有一个私有dbcontext字段,并且我覆盖控制器的dispose方法来调用上下文中的dispose

但是我有时也会在我的其他一些类中对db进行查询,这些类可以从控制器中调用,也可以打开一个上下文。

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

3 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:3)

使用依赖注入

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

在我的最新项目中,我已经组织了一些事情,以便我几乎完成了项目,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);
    }
}

答案 2 :(得分:2)

我建议您传递某种工厂类的实例,而不是传递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<T>类型的属性。

要从Entity框架中完全抽象,您可以创建另一个抽象层,例如使用UnitOfWork或Repository模式方法,但我想前者涵盖了您现在关注的问题。