我现在一直在使用实体框架,我遇到过两个场景,两个上下文会尝试访问同一个实体等,所以我想知道是否我不打开/以最佳方式关闭我的dbcontexts。
目前我基本上在每个控制器上打开一个DbContext,就像最初设置基本MVC应用程序一样,这意味着我在控制器上有一个私有dbcontext字段,并且我覆盖控制器的dispose方法来调用上下文中的dispose
但是我有时也会在我的其他一些类中对db进行查询,这些类可以从控制器中调用,也可以打开一个上下文。
有没有办法在没有显式处理程序的情况下访问开放上下文?通过十几种不同的方法传递DbContext引用对我来说真的没有意义。
答案 0 :(得分:3)
处理DbContext
实例的“最佳”方法是使其成为需要它的每个方法的参数(或者,如果整个类需要它,则为构造函数的参数)。
这是IoC(控制反转)的基本部分,它允许调用者为方法指定依赖关系,从而允许调用者“控制”被调用方法的行为。
然后,您还可以添加依赖注入框架。可以将它们配置为使用DbContext
的单例实例,并将该实例注入任何需要它的方法。
答案 1 :(得分:3)
使用依赖注入
正如其他人已经说过并且可能会重申的那样,“正确的方式”通常被认为是依赖注入。
在我的最新项目中,我已经组织了一些事情,以便我几乎完成了项目,DI已经非常轻松,我自己就是这样做的(而不是使用注射器)。其中一个主要因素是严格遵守这一结构:
WebProject
| |
| DataServices
| | |
ViewModels EntityModels
在一个工作单元期间访问所有数据服务是通过单个DataServiceFactory实例进行的,该实例需要一个MyDbContext实例。另一个因素是完全RESTful的应用程序设计 - 这意味着我不必在整个代码中散布持久性功能。
没有依赖注入
那就是说,也许DI不适合你这个项目。也许:
在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模式方法,但我想前者涵盖了您现在关注的问题。