问题 如何确定Dbcontexts的范围(防止整个应用程序的单例上下文)


我想知道如何在实体框架中扩展您的Dbcontexts,这样您就不会在整个应用程序中使用单个Dbcontext。我是Entity Framework的新手并且一直在阅读教程,但他们都使用单个Dbcontext作为示例,所以EF现在几乎是我的黑盒子。

比方说,我有3个型号:

  • 岗位
  • 用户
  • 评论

每个模型彼此相关(帖子属于用户,评论属于用户和帖子)。我是否为每个人单独制作一个Dbcontext?但这不正确,因为它们都是相关的,或者我会为我需要的每个场景制作Dbcontext?例如,如果我只需要查询Post和Comments而不是用户,那就是PostCommentsContext。然后我们会有一个PostUserCommentContext ...


8000
2018-01-27 21:47


起源

此外,当您将UnitOfWork与ORM一起使用时,您需要注意,如果提交失败,那么您需要一个新的Context / Unit of Work lavinski.tumblr.com/post/9114111237/... - Daniel Little


答案:


最好的解决方案是使用a 工作单位 包装数据上下文,以及管理连接生命周期并允许您使用多个存储库(如果您倾向于沿着这条路径前进)。

实施摘要:

  • 创建一个界面(IUnitOfWork)暴露你的属性 DbSet的,以及一个叫做的方法 承诺
  • 创建一个实现(EntityFrameworkUnitOfWork),按要求实施。提交只是打电话 保存更改 在基类(DbContext),并为最后一分钟的逻辑提供了一个很好的挂钩。
  • 你的控制器接受了 IUnitOfWork,使用DI(最好)解决一个问题 EntityFrameworkUnitOfWork,用 HTTP上下文作用域生存期 设置 (StructureMap 对此有好处)
  • (可选,但建议)创建一个 知识库 这也需要 IUnitOfWork,并通过您的控制器解决这个问题。

HTH

编辑 - 回应评论

哦,你怎么能做涉及在多个模型中创建记录的工作呢?即,在同一事务中创建新用户和新帖子。

鉴于您使用ASP.NET MVC,您的控制器应该接受 IUnitOfWork 在他们的构造函数中。

这是一个例子,基于你的要求

public SomeController : Controller
{
   private IUnitOfWork _unitOfWork;
   private IUserRepo _userRepo;
   private IPostRepo _postRepo;

   public SomeController(IUnitOfWork unitOfWork, IUserRepo userRepo, IPostRepo postRepo)
   {
      _unitOfWork = unitOfWork; // use DI to resolve EntityFrameworkUnitOfWork
      _userRepo = userRepo;
      _postRepo = postRepo;
   }

   [HttpPost]
   public ActionResult CreateUserAndPost(User user, Post post)
   {
      // at this stage, a HTTP request has come in, been resolved to be this Controller
      // your DI container would then see this Controller needs a IUnitOfWork, as well
      // as two Repositories. DI smarts will resolve each dependency.
      // The end result is a single DataContext (wrapped by UoW) shared by all Repos.
      try
      {
         userRepo.Add(user);
         postRepo.Add(post);
         // nothing has been sent to DB yet, only two objects in EF graph set to EntityState.Added
         _unitOfWork.Commit(); // two INSERT's pushed to DB
      }
      catch (Exception exc)
      {
          ModelState.AddError("UhOh", exc.ToString());
      }
   }
}

还有一个问题,HTTP上下文范围的生命周期是做什么的?

DI-talk中的对象具有范围管理设置,包括每个线程,每个会话,每个http请求,单例等。

HTTP-context scoped是Web应用程序的推荐设置。它意味着“在HTTP请求进入时新建上下文,并在请求完成时删除它”。


7
2018-01-27 22:39



我目前正在使用存储库模式。但是,如果使用工作单元模式,那么存储库是否应该在UoW中?如果你在多个模型中工作(因此多个存储库)会怎么样? - Alex
@Alex--你可以这样做,是的 - 但是我的偏好是保持它们是分开的,并且让存储库应该在他们的ctor中占用一个单元,这是事先创建的(当一个HTTP请求进来时由一个DI容器) 。明白我的意思了吗? - RPM1984
哦,你怎么能做涉及在多个模型中创建记录的工作呢?即,在同一事务中创建新用户和新帖子。还有一个问题,HTTP上下文范围的生命周期是做什么的?谢谢! - Alex
@Alex - 看我的编辑 - RPM1984
这似乎在Web应用程序中工作正常,但在单线程或多线程应用程序中真的不会有用。 - Rushino


使用1个DbContext!这将使你的生活更轻松。不要担心性能,不会加载不需要或查询的数据,也不会消耗任何资源。

public class UserContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Comment> Comments { get; set; }
}

对于某些情况,您可能需要2个或更多上下文。

像上面那样的上下文来保存应用程序工作所需的所有前端数据,以及另一个上下文 - 例如 - 存储从该前端数据生成的报告,并且仅用于后端你的申请。


6
2018-01-27 21:56



赞成成为唯一没有建议包装的答案 DbContext 和一些 IUnitOfWork :) - Mathieu Guindon


我正在试验UnitofWork,这是我想出来的......

首先,我创建了一个只包含一个方法的IUnitofWork。 Commit();

然后我的dbContext看起来像这样

public class myContext : DbContext, IUnitOfWork
{
    public DbSet<Users> Users { get; set; }
    public DbSet<Addresses> Address { get; set; }

    public void Save()
    {
        SaveChanges();
    }
}

我的存储库类在他们的ctors中使用UnitofWork。

public class UserRepository : IRepository<Position>    
{
    private myContext _context;

    public UserRepository (IUnitOfWork unitOfWork)
    {
        if (unitOfWork == null)
            throw new ArgumentNullException("unitOfWork");

        _context = unitOfWork as myContext;
    }
    /// other methods ///
}

那么控制器中的代码就是这样的

_unitOfWork = new myContext();
_userDB = new UserRepository(_unitOfWork);
_addressDB = new AddressRepository(_unitOfWork);
_userDB.Add(newUser);
_addresesDB.Add(newAddress);
_unitOfWork.Save();

我已经调试并证明在调用_unitOfWork的Save方法之前不会提交任何数据。很酷的东西!!


0
2018-03-31 15:33