Открытие и закрытие соединения с базой данных внутри транзакции

Я разработал часть доступа к данным нашей структуры так, чтобы каждый раз, когда бизнес-объект (BO) должен взаимодействовать с базой данных, он должен был бы открыть соединение, вызвать уровень доступа к данным (для выполнения запроса), а затем закрыть соединение. Затем, если это необходимо для запуска в транзакции, он откроет соединение, начнет транзакцию, вызовет уровень доступа к данным (для выполнения запроса) , а затем зафиксирует транзакцию, закроет транзакцию и, наконец, закроет соединение.

Я сделал это таким образом в мышлении "открыть поздно, закрываются рано"... но что если мне нужно позвонить других ЛС для отправки данных в одной транзакции? Есть ли лучший способ обработки открытия и закрытия соединений, а также работы с транзакциями?

Я новичок в разработке архитектуры приложений, поэтому я надеюсь, что я не делаю это неправильно... любая помощь приветствуется.

5 ответов


Если данный бизнес-объект должен выполнять различные методы в транзакции, используйте TransactionScope вот так:

using ( var transactionScope = new TransactionScope() )
{
    this.Save();
    childObjA.Save();
    childObjB.Save();
    childObjC.Save();
    childObjD.Save();

    transactionScope.Complete();
}

Если какой-либо из объектов создает исключение, он будет откат транзакции.

посмотреть справочная страница MSDN для TransactionScope дополнительные.


когда абстракции более высокого уровня зависят от абстракций более низкого уровня (например, классы бизнес-логики в зависимости от подключений к данным), обычно предоставляют абстракции более низкого уровня через конструктор. Техника называется конструктор инъекций:

public class OrderService
{
    private SqlConnection connection;

    public OrderService(SqlConnection connection)
    {
        if (connection == null)
            throw new ArgumentNullException("connection");
        this.connection = connection;
    }

    // Other methods
}

Это затем позволяет писать код против служб, подобных следующим:

using (TransactionScope tsc = new TransactionScope())
using (SqlConnection connection = new SqlConnection(...))
{
    connection.Open();
    OrderService os = new OrderService(connection);
    os.ProcessOrder(myOrder);
    ShippingService ss = new ShippingService(connection);
    ss.ShipOrder(myOrder);
    tsc.Complete();
}

что, скорее всего, будет то, что вы хотите, в конце - возможность поделиться одно соединение среди многих услуг.

Это также помогает отделения ваши услуги из деталей реализации подключения к данным. Таким образом, если вы хотите сделать что-то вроде изменения настроек соединения при определенных обстоятельствах, вам не нужно копаться в деталях 50 различных служб, вам нужно только изменить одну строку кода, которая создает соединение.

еще одна вещь: если вы собираетесь использовать TransactionScope обязательно добавить Transaction Binding=Explicit Unbind к строке подключения, иначе на самом деле можно получить несогласованные данные, если время ожидания транзакции истекло.


а, TransactionScope - это путь.

Если вы используете SQL Server 2008 и .NET 3.5, я бы изменил дизайн, чтобы бизнес-объект управлял транзакцией и оставил Открытие и закрытие соединения со слоем данных.

при включении пула соединений вы фактически не будете нести накладные расходы на открытие физического подключения к базе данных, и ваши соединения будут открыты только при выполнении фактической работы. Поскольку (я предположил) у вас есть SQL Server 2008 с .NET 3.5 ваша транзакция не будет перерастать в распределенную транзакцию (если открыто несколько соединений одновременно), так что вы получаете лучшее из обоих миров.

тогда вы можете написать свой бизнес-объект следующим образом:

using (TransactionScope transactionScope = new TransactionScope())
{
    DataObject dataObject = new DataObject();
    dataObject.UpdateQuantity(...);

    ShippingManager shippingManager = new ShippingManager();
    shippingManager.ShipOrder(...);

    transactionScope.Complete()
}

это позволяет избежать необходимости передавать строки подключения ко всем бизнес-объектам и координировать транзакции простой.

обновление

красота системы.Транзакции заключается в том, что все транзакции управляются для вас независимо от используемого соединения. Вы просто объявляете TransactionScope и весь доступ к базе данных в пределах этого TransactionScope будет происходить с одной транзакцией (если вы не запросите иначе с различными настройками TransactionScope).

в прошлом (SQL Server 2005 .NET 2.0), Если вы открыли и закрыли соединение, а затем открыл и закрыл другое соединение (даже с той же строкой соединения), то транзакция была повышена от облегченной транзакции к распределенной транзакции. Это было нежелательно, потому что страдает производительность (связь с MSDTC выходит из процесса и двухфазный протокол фиксации), и MSDTC может быть болью для настройки во многих производственных средах (брандмауэры и безопасность).

С SQL Server 2008 и .NET 3.5 они добавили возможность избегайте этой акции при открытии и закрытии нескольких соединений с одной и той же строкой соединения в рамках одной транзакции. Для действительно хорошего объяснения того, что они видели расширение облегченных транзакций в SqlClient.

обновление 2

транзакции с Oracle 10g будут правильно работать с TransactionScope. И это выглядит как ODP.NET поддерживает легкие транзакции (что приятно). К Сожалению, Я подумайте, что продвижение к распределенной транзакции произойдет с закрытием и открытием соединений.

Если вы хотите избежать распределенной транзакции, вы можете передать соединение каждому вызову метода / бизнес-объекту. Если вы не хотите передавать соединение, вы можете использовать класс ConnectionScope который держит соединение открытым в потоке. Альтернативой этому может быть использование блока приложений доступа к данным Enterprise Library 3.0 (и выше). The блок доступа к данным может обнаружить, что транзакция выполняется и использовать то же соединение чтобы избежать распределенной транзакции.


похоже, у вас есть правильная идея. Если необходимо задействовать несколько лс, то один из них должен быть "контроллером" - он должен открывать и закрывать соединение и передавать его другим. Или какой-то объект" обертки " может обрабатывать соединение и передавать его каждому из ЛС. Ваши ЛС, возможно, должны быть разработаны, чтобы работать как самостоятельно (обрабатывать свое собственное соединение), так и принимать существующее соединение извне.


вы, вероятно, ищет единица работы шаблон и реестр узор. Эти два шаблона могут работать совместно, чтобы разделить проблемы поиска бизнес-объектов и отслеживания их для последующей фиксации в хранилище данных в качестве транзакции.

Я бы также посмотрел на реляционное сопоставление объектов или ORM. ORM-это более высокий уровень состава единицы работы, реестра, постоянства невежества и других шаблонов, который обеспечивает очень чистый разделение бизнес-логики от логики. Используя и ORM, вы можете вообще исключить потребность написать хранимые процедуры, построить изготовленный на заказ DAL, etc. ORM заботится о ваших проблемах с настойчивостью, позволяя вам сосредоточиться на бизнес-логике, которую нужно сделать.

поскольку вы используете C# и .NET, я бы посмотрел на Entity Framework (v4, не используйте v1) или LINQ to SQL. Оба или картографы, которые поставляются с .NET framework из v3.5 и далее. LINQ to SQL-очень простой и хорошо обработанный ORM, который должен заставить вас работать очень быстро. Entity Framework-гораздо более богатый ORM, который также очень хорошо оснащен (лучше, чем LINQ to SQL), и предлагает значительно больше функций. Есть также сторонние ORM, которые могут выполнять эту работу, в том числе бесплатный под названием NHibernate. Хотя это не так хорошо, как Microsoft ORM, NHibernate - очень зрелый ORM с открытым исходным кодом с большим сообществом.

Если ORM не является возможностью, тогда я бы заглянул в единица работы, реестр (или репозиторий), упорство невежество,разделение, Единая Ответственность и другие связанные шаблоны.