较长的数据上下文或上下文之外的交易之间的权衡?

时间:2016-03-06 00:14:48

标签: c# entity-framework asp.net-web-api transactions

我正在编写一个Web API Rest服务,它在一组不同的实体上执行操作。我把它们打破了这样:

        db = new DBEntities();

        using (var dbContextTransaction = db.Database.BeginTransaction())
        {
            try
            {
                ProcessClient();
                ProcessClientPerson();
                ProcessGuardian();
                ProcessAddress();
                ProcessEmail();
                ProcessPhones();
                ProcessChildren();

                dbContextTransaction.Commit();
            }
            catch (Exception ex)
            {
                dbContextTransaction.Rollback();

            etc.

遵循数据上下文应尽可能短的建议,每个方法都创建自己的数据上下文,调用SaveChanges(),并在最后处置它:

  private ProcessClient()
  {
        db = new DBEntities();
        ....

这显然不起作用 - 以这种方式创建的事务上下文与数据上下文相关联。如果其中一个实体操作出现问题,那么只会回滚(隐式)该操作,但总体事务不会回滚。

我发现了这个approach for creating a transaction outside of EF,但我想知道我是否应该遵循它,或者我是否应该在交易期间让我的数据上下文生效并将交易保留在EF中??

我不是在寻找意见,而是寻找有关稳定性,性能等的数据

2 个答案:

答案 0 :(得分:1)

没有立即需要保持上下文的短命。您可以这样做,但您不必这样做。

随着时间的推移,实体将在上下文中累积。如果您冒着内存不足的风险,可能需要放开上下文。

否则,通常的过程是在逻辑工作单元的持续时间内使上下文保持活动状态。在这里,UOW就是完整的所有方法。

这也使事务管理变得更容易(正如您已经发现的那样)。

  

dbContextTransaction.Rollback();

这是一种反模式。如果出现错误,请不要提交。

答案 1 :(得分:0)

我对此感到复杂。我正在处理没有外键约束的遗留数据库,我在其中一个服务调用中插入,更新和删除20到30个对象。

问题是我需要经常调用SaveChanges()来获取将成为外键的标识列值。

另一方面,如果出现三层故障,我必须能够回滚所有内容,因此需要一个大型事务。

由于某些我无法确定的原因,在相同的数据上下文中重复调用SaveChanges会导致连接状态打开的错误。所以我最终还是为每个方法提供了自己的数据上下文:

var scope = new TransactionScope(TransactionScopeOption.RequiresNew,
new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted);

using (scope)
{
  try
  {
      ProcessClient();
      ProcessClientPerson();
      ProcessGuardian();
      ProcessAddress();
      ProcessEmail();
      ProcessPhones();
      ProcessChildren();

    scope.Complete();
  }
  catch (System.Data.Entity.Validation.
               DbEntityValidationException ex)
  {
  [...] handle validation errors etc [...]
  }
}

每个部分基本上都是这样做的,一旦被剥离到最基本的要点:

private void ProcessClient() {
{
   using (MyDBEntities db = new MyDBEntities())
   {

     [...] doing client stuff [...]

     aClient.LastUpdated = DateTime.Now;

     db.AddOrUpdate(db, db.Clients, aClient, aClient.ClientID);
     db.SaveChanges();

     ClientId = aClient.ClientID;   // now I can use this to form FKs
   }
}

关于锁定的复杂感觉,因为在我的开发虚拟机上,事务运行1-2秒,这是一个生产数据库,办公人员和在线客户同时通过Web应用程序进行CRUD交易。

不相关,但对我的AddOrUpdate方法was this blog post很有帮助。