1

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

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

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

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

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

4

2 回答 2

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();  
 }
于 2014-01-08T23:44:35.353 回答
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
        {
        }
    }
}
于 2014-01-13T06:34:48.997 回答