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

我现在一直在使用entity framework,并且我遇到了两个场景,其中两个上下文会尝试访问同一个实体等,所以我想知道是否我可能没有打开/关闭我的dbcontexts以最好的方式。

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

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

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

使用dependency injection

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

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

  WebProject | | | DataServices | | | ViewModels EntityModels 

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

没有dependency injection

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

  • 你不打算写unit testing
  • 你需要更多时间来理解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); } } 

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

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

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

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

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

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

然后工厂返回你的实例:

 public interface IContextFactory { IDbContext Retrieve(); } 

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

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