如何将多个业务交易包装在另一个交易下?

How can I wrap multiple business transactions under another transaction?

在我的项目中,单个服务方法就是一个业务事务。例如,假设我有以下 Service/Repository:

public interface IDocumentService {

    void CreateDocument(Document doc);
    void AttachFileToDocument(int documentId, string filepath);

}

public class DocumentService 
{
    private readonly IDocumentRepository _repository;

    public DocumentService(IDocumentRepository repository)
    {
        _repository = repository;
    }

    public void CreateDocument(Document doc)
    {
        // do some validation
        // ..

        // create entity object....direct mapping, automapper, whatever
        DocEntity entity = new DocEntity()
        entity.Name = doc.Name;
        // etc

        using (var db = new DbEntities())
        {
            _repository.Insert(db, doc);
            _repository.AddSource(db, entity, doc.Sheet.SourceId);

            db.SaveChanges();
        }
    }

    public void AttachFileToDocument(int documentId, string filepath)
    {
        using (var context = new DbEntities())
        {
            DocEntity doc = _repository.GetById(context, id);
            // validation etc

            using (var tran = context.Database.BeginTransaction())
            {
                try
                {
                    // ..
                    // determine filename
                    // copy file
                    // update pointer to file in doc database
                    // ..

                    // save changes
                    context.SaveChanges();

                    tran.Commit();
                }
                catch (System.Exception)
                {
                    tran.Rollback();
                    throw;
                }
            }
        }
    }
}

这是两个不同的东西,所以他们有自己的方法。然而,有时但不是全部,它们都是必需的并且需要是原子的。我试图创建一个新的服务方法,将它们包装在一个事务中,但它不会回滚数据库创建。

我还能如何使用相同的通用设计模式实现此目的 - 或者在这种情况下这不是一个好的模式 - 每个业务交易一个上下文?

您需要使这两种方法共享相同的数据库连接和事务。怎么做是你的选择。

您的代码看起来像反模式。您应该为每个工作单元使用一个 ObjectContext,其中 UOW 范围应包含您需要做的所有事情。通常,范围是 HTTP 请求。

如果你这样做,问题就会消失。但你也可以用任何你喜欢的方式解决它,只要你共享相同的连接和事务。

考虑使用 TransactionScope 将事务语义与其余部分分开。小心 - 您仍然需要共享一个连接,否则您将升级为分布式事务。