Открытие и закрытие соединения с базой данных внутри транзакции
Я разработал часть доступа к данным нашей структуры так, чтобы каждый раз, когда бизнес-объект (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 не является возможностью, тогда я бы заглянул в единица работы, реестр (или репозиторий), упорство невежество,разделение, Единая Ответственность и другие связанные шаблоны.