Использование TransactionScope несколько раз - PullRequest
2 голосов
/ 26 сентября 2011

Это правильный способ использования области транзакции:

У меня есть объект, который представляет часть вещи:

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, это не должно иметь значения

Верны ли мои предположения?

Не обращайте внимания на что-либо о структуре объектов или ответственности за постоянство, это пример, который поможет мне понять, как я должен делать вещи. Отчасти потому, что кажется, что он не работает, когда я пытаюсь заменить oracle на SqlLite в качестве фабрики провайдеров db, и мне интересно, где мне следует провести расследование.

1 Ответ

3 голосов
/ 26 сентября 2011

Ответы на ваши вопросы (и я предположил, что Microsoft SQL Server 2005 или более поздняя версия):

  1. Соединения не будут новыми и повторно использованы из пула

    • Это зависит - например, то же самое соединение будет повторно использоваться для последовательных шагов в вашей совокупной транзакции, если все соединения будут с одной и той же БД с одинаковыми учетными данными и если SQL сможет использовать облегченный диспетчер транзакций (SQL 2005 и более поздние версии). (но пул соединений с SQL все еще работает, если вы об этом спрашивали?)
  2. Atomic SavePart - да, это будет работать ACID, как и ожидалось.
  3. Да, вложение TransactionScopes с той же областью действия также будет атомарным. Транзакция будет подтверждена, только когда завершен внешний TS.
  4. Да, также атомарно, но учтите, что вы будете наращивать блокировки SQL. Если имеет смысл фиксировать каждое Thing (и его ThingParts) по отдельности, это будет предпочтительнее с точки зрения параллелизма SQL.
  5. Поставщик должен быть совместимым с менеджером ресурсов TransactionScope (и, возможно, также совместимым с DTC). например не перемещайте вашу базу данных в Rocket U2 и ожидайте, что TransactionScopes будут работать.

Всего одна ошибка - new TransactionScope () по умолчанию имеет уровень изоляции READ_SERIALIZABLE - это часто слишком пессимистично для большинства сценариев - READ COMMITTED обычно более применимо.

...