多次使用TransactionScope

时间:2011-09-26 10:51:10

标签: transactions connection connection-pooling transactionscope

这是使用交易范围的正确方法吗?

我有一个代表事物一部分的对象:

public class ThingPart
{
    private DbProviderFactory connectionFactory;

    public void SavePart()
    {
        using (TransactionScope ts = new TransactionScope()
        {
            ///save the bits I want to be done in a single transaction
            SavePartA();
            SavePartB();
            ts.Complete(); 
        }
    }

    private void SavePartA()
    {
        using (Connection con = connectionFactory.CreateConnection()
        {
            con.Open();
            Command command = con.CreateCommand();
            ...
            command.ExecuteNonQuery();             
        }
    }

    private void SavePartB()
    {
        using (Connection con = connectionFactory.CreateConnection()
        {
            con.Open();
            Command command = con.CreateCommand();
            ...
            command.ExecuteNonQuery();             
        }
    }
}

代表事情的东西:

public class Thing
{
    private DbProviderFactory connectionFactory;

    public void SaveThing()
    {
        using (TransactionScope ts = new TransactionScope()
        {
            ///save the bits I want to be done in a single transaction
            SaveHeader();
            foreach (ThingPart part in parts)
            {
                part.SavePart();
            }  
            ts.Complete();    
        }
    }

    private void SaveHeader()
    {
        using (Connection con = connectionFactory.CreateConnection()
        {
            con.Open();
            Command command = con.CreateCommand();
            ...
            command.ExecuteNonQuery();             
        }
    }
}

我也有管理很多东西的东西

public class ThingManager
{    
    public void SaveThings
    {        
        using (TransactionScope ts = new TransactionScope)
        {            
            foreach (Thing thing in things)
            {
                thing.SaveThing();
            }            
        }        
    }    
}

我的理解是:

  • 连接不是新的,每次都会从池中重用(假设DbProvider支持连接池并且已启用)
  • 如果我刚刚调用ThingPart.SavePart(来自任何其他类的上下文之外),那么事务将是这样的,那么A和B部分既可以保存,也可以保存。
  • 如果我呼叫Thing.Save(来自任何其他类的上下文之外),那么标题和所有部分将全部保存或不保存,即一切都将在同一事务中发生
  • 如果我打电话给ThingManager.SaveThings,那么我的所有东西都会被保存,或者一切都没有,即一切都会在同一笔交易中发生。
  • 如果我更改了所使用的DbProviderFactory实现,则不应该有所作为

我的假设是否正确?

忽略关于对象结构或持久性责任的任何事情,这是一个帮助我理解我应该如何做事的例子。部分是因为当我尝试用SqlLite替换oracle作为db提供程序工厂时它似乎无法工作,我想知道我应该在哪里花时间调查。

1 个答案:

答案 0 :(得分:3)

回答你的子弹(我假设是Microsoft SQL Server 2005或更高版本):

  1.   

    连接不会是新的,并且会从池中重复使用

    • 这取决于 - 例如如果所有连接都使用相同的凭据,并且SQL能够使用轻量级事务管理器(SQL 2005及更高版本),则SAME连接将重复用于聚合事务中的后续步骤。 (但是,如果你问的是SQL连接池仍然有用吗?)
  2. Atomic SavePart - 是的,这将按预期运行ACID。
  3. 是嵌套具有相同范围的TransactionScopes也将是原子的。只有在最外面的TS完成时才会提交。
  4. 是的,也是原子的,但请注意,您将升级SQL锁。如果单独提交每个Thing(及其ThingParts)是有意义的,那么从SQL并发的角度来看,这将是更好的。
  5. 提供者需要兼容TransactionScope资源管理器(也可能是DTC兼容)。例如不要将数据库移动到Rocket U2并期望TransactionScopes正常工作。
  6. 只有一个问题 - new TransactionScope() defaults to isolation level READ_SERIALIZABLE - 对于大多数情况来说,这通常是过于悲观 - 通常认为READ COMMITTED更适用。