【问题标题】:Repository Design: Sharing a transaction存储库设计:共享事务
【发布时间】:2014-01-08 21:31:43
【问题描述】:

我正在使用 ServiceStack 实现一个 Rest 服务。我们使用存储库模式并通过 IOC 将存储库自动连接到服务中。

目前,我们有一种简单的方法,即一个数据库模型与一个存储库配对。这意味着只要在一项服务中操纵多个实体,就不会使用事务边界。存储库是按顺序调用的:如果一个或多个步骤失败,则必须手动将数据库“回滚”到其初始状态。最坏的情况是,如果请求线程终止,或者如果发生未经检查的异常(例如,OutOfMemoryException),数据库将处于不一致的状态。

我有一组假设的解决方案,但我认为没有一个是足够的:

  1. 打开连接并在服务级别启动事务。调用存储库,将连接传递给它们。这显然是错误的,因为它违反了所有 ddd 设计准则。关键是上层完全不知道具体的持久性。此外,它会扰乱单元测试。
  2. 让第一个存储库启动事务。将调用其他依赖存储库,但传递已打开的连接。这听起来也是糟糕的设计。
  3. 定义聚合。因为我不是领域建模专家,所以我不是这个的忠实粉丝,而且我觉得通过引入聚合,我很容易引入设计错误。当前模型的一个优点是简单。

有人对这个问题有建议吗? 提前致谢

【问题讨论】:

    标签: servicestack repository-pattern ormlite-servicestack repository-design


    【解决方案1】:

    您可以使用通常称为 UnitOfWork 的传递类,您将在其中打开和关闭“连接”。搜索“工作单元”你会发现很多例子。您可以自定义以下 sn-p 以包含事务。

    public class UnitOfWork : IUnitOfWork
    {
        readonly CompanyDbContext _context;
    
        public UnitOfWork()
        {
            _context = new CompanyDbContext ();
        }
    
        private bool _disposed;
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            _disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        public void Save()
        {
            _context.SaveChanges();
        }
    
        public IProductRepository ProductRepository
        {
            get { return new ProductRepository(_context); }
        }
    
        public ICartRepository CartRepository
        {
            get { return new CartRepository(_context); }
        }
    }
    

    然后你可以像下面这样进行多笔交易

     using (_unitOfWork)
     {
         var p = _unitOfWork.ProductRepository.SingleOrDefault(a => a.id ==1);
         _unitOfWork.CartRepository.Add(p);
         _unitOfWork.Save();  
     }
    

    【讨论】:

      【解决方案2】:

      为了在 Ormlite 中有效地使用事务,您需要创建自定义 DBManager 类,该类可以保存每个线程的连接对象(使用 ThreadStatic)。然后您可以在您的存储库中使用此自定义 DBManager 来调用不同的 ormlite 函数。

      我使用的部分代码是(需要修改代码才能正常工作):

      public class ThreadSpecificDBManager : IDisposable, IDBManager
      {
          [ThreadStatic]
          private static int connectionCount = 0;
      
          [ThreadStatic]
          private static int transactionCount = 0;
      
          [ThreadStatic]
          private static IDbConnection connection = null;
      
          public string ConnectionString { get; set; }
      
          public IDbConnection Connection { get { EnsureOpenConnection();  return connection; } }
      
          static ThreadSpecificDBManager()
          {
          }
      
          private void EnsureOpenConnection()
          {
              if ((connection == null) || (connection.State == ConnectionState.Closed))
              {
                  OrmLiteConfig.TSTransaction = null;
                  transactionCount = 0;
                  connectionCount = 0;
      
                  connection = (DbConnection)ConnectionString.OpenDbConnection();
      
                  //if (ConfigBase.EnableWebProfiler == true)
                  //    connection = new ProfiledDbConnection((DbConnection)connection, MiniProfiler.Current);
              }
          }
      
          public ThreadSpecificDBManager(string connectionString)
          {
              ConnectionString = connectionString;
              connectionCount++;
              EnsureOpenConnection();
          }
      
          public void Dispose()
          {
              if (transactionCount > 0)
              {
                  //Log.Error("Uncommitted Transaction is left");
              }
      
              connectionCount--;
              if (connectionCount < 1)
              {
                  if ((connection != null) && (connection.State == ConnectionState.Open))
                      connection.Close();
      
                  if (connection != null)
                      connection.Dispose();
      
                  connection = null;
              }
          }
      
          public void BeginTransaction()
          {
              if (transactionCount == 0)
              {
                  //Log.SqlBeginTransaction(0, true);
                  OrmLiteConfig.TSTransaction = Connection.BeginTransaction();
              }
              else
              {
                  //Log.SqlBeginTransaction(transactionCount, false);
              }
              transactionCount = transactionCount + 1;
          }
      
          public void RollbackTransaction()
          {
              try
              {
                  if (transactionCount == 0)
                  {
                      //Log.SqlError("Transaction Rollback called without a begin transaction call.");
                      return;
                  }
      
                  if (OrmLiteConfig.TSTransaction == null)
                  {
                      //Log.SqlError("Transaction is not saved in the Thread Safe variable- so it cannot be rollbacked.");
                      throw new Exception("Transaction is not saved in the Thread Safe variable- so it cannot be rollbacked.");
                  }
      
                  if (transactionCount == 1)
                  {
                      transactionCount = 0;
                      try
                      {
                          //Log.SqlRollbackTransaction(transactionCount, true);
                          OrmLiteConfig.TSTransaction.Rollback();
                      }
                      catch (Exception ex1)
                      {
                          //Log.SqlError(ex1,"Error when rolling back the transaction");
                      }
      
                      OrmLiteConfig.TSTransaction.Dispose();
                      OrmLiteConfig.TSTransaction = null;
                  }
                  else
                  {
                      //Log.SqlRollbackTransaction(transactionCount, false);
                      transactionCount = transactionCount - 1;
                  }
              }
              finally
              {
      
              }
          }
      
          public void CommitTransaction()
          {
              try
              {
                  if (transactionCount == 0)
                  {
                      //Log.SqlError("Transaction Rollback called without a begin transaction call.");
                      return;
                  }
      
                  if (OrmLiteConfig.TSTransaction == null)
                  {
                      //Log.SqlError("Transaction is not saved in the Thread Safe variable- so it cannot be rollbacked.");
                      throw new Exception("Transaction is not saved in the Thread Safe variable- so it cannot be rollbacked.");
                  }
      
                  if (transactionCount == 1)
                  {
                      //Log.SqlCommitTransaction(transactionCount,true);
                      transactionCount = 0;
                      OrmLiteConfig.TSTransaction.Commit();
                      OrmLiteConfig.TSTransaction.Dispose();
                      OrmLiteConfig.TSTransaction = null;
                  }
                  else
                  {
                      //Log.SqlCommitTransaction(transactionCount, false);
                      transactionCount = transactionCount - 1 ;
                  }
              }
              finally
              {
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-04-08
        • 1970-01-01
        • 2020-10-08
        • 2014-03-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多