Открытие и закрытие соединения с базой данных внутри транзакции - PullRequest
9 голосов
/ 02 апреля 2010

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

Я сделал это таким образом: «поздно поздно, рано закрыто» … но что, если мне нужно будет позвонить другим BO для отправки данных в одной транзакции? Есть ли лучший способ справиться с открытием и закрытием соединений, а также работать с транзакциями?

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

Ответы [ 5 ]

5 голосов
/ 02 апреля 2010

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

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

    transactionScope.Complete();
}

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

Подробнее см. справочную страницу MSDN для TransactionScope.

4 голосов
/ 02 апреля 2010

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

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

2 голосов
/ 02 апреля 2010

Как уже упоминалось, 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 Application 3.0 (и выше) блока доступа к данным. Блок доступа к данным может обнаружить, что транзакция выполняется, и использовать то же соединение , чтобы избежать распределенной транзакции.

2 голосов
/ 02 апреля 2010

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

1 голос
/ 02 апреля 2010

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

Я бы также посмотрел на Object Relational Mapping или ORM. ORM - это высокоуровневая структура единицы работы, реестра, невежественности и других шаблонов, которая обеспечивает очень четкое отделение бизнес-логики от логики постоянства. Используя ORM, вы, как правило, можете избавить вас от необходимости писать хранимые процедуры, создавать пользовательские DAL и т. Д. ORM заботится о ваших проблемах с постоянством, позволяя вам сосредоточиться на бизнес-логике, которая должна быть выполнена.

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

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

...