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

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

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

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

9
задан stakx supports GoFundMonica 16 August 2012 в 09:18
поделиться

5 ответов

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

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

    transactionScope.Complete();
}

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

См. справочную страницу MSDN для TransactionScope для получения дополнительной информации.

5
ответ дан 4 December 2019 в 14:27
поделиться

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

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 в строку подключения, иначе это действительно может закончиться с противоречивыми данными, если время транзакции истекло.

4
ответ дан 4 December 2019 в 14:27
поделиться

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

2
ответ дан 4 December 2019 в 14:27
поделиться

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

Я бы также изучил объектно-реляционное отображение или ORM. ORM - это композиция более высокого уровня, состоящая из единицы работы, реестра, игнорирования персистентности и других шаблонов, которые обеспечивают очень четкое разделение вашей бизнес-логики от вашей логики сохраняемости. Используя ORM, вы, как правило, можете избавиться от необходимости писать хранимые процедуры, создавать собственный DAL и т. Д. 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. Хотя NHibernate не так хорошо проработан, как ORM от Microsoft, это очень зрелая ORM с открытым исходным кодом, пользующаяся большим успехом в сообществе.

Если ORM невозможен, я бы посмотрел в Единицу работы , Реестр (или Хранилище), Невежество настойчивости, Разделение проблем , Single Responsibility и другие связанные шаблоны.

1
ответ дан 4 December 2019 в 14:27
поделиться

Как упоминалось другими, 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()
}

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

Обновление

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

2
ответ дан 4 December 2019 в 14:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: