1

我正在使用ServiceStack实现Rest服务。我们通过IOC使用存储库模式和自动连线存储库到服务中。存储库设计:共享事务

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

我有一组假想的解决方案,但我认为没有充足:

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

任何人都有这个问题的建议吗? 在此先感谢

回答

2

您可以使用通常称为UnitOfWork的通过类,您将打开并关闭“连接”。搜索“工作单元”,你会发现很多例子。您可以自定义以下片段以包含交易。

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(); 
} 
1

为了与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 
     { 
     } 
    } 
}