Почему вы используете TransactionScope для вызовов базы данных только для чтения в NET C#? - PullRequest
2 голосов
/ 28 февраля 2020

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

Например:

    public IEnumerable<IEvent> GetFullEventHistory()
    {
        using (new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { Timeout = TimeSpan.Zero, IsolationLevel = IsolationLevel.ReadCommitted }))
        {
            var events = _store.GetFullEventHistory();
            IList<IEvent> deserialisedEvents = GetEvents(events);
            return deserialisedEvents;
        }
    }

Мне нравится убирать блок using () содержащий TransactionScope. Это вызов базы данных только для чтения, поэтому транзакция ничего не делает.

Это правильное предположение? Или я что-то здесь упускаю?

Кстати ... В вызовах Write DB не указан TransactionScope.

Ответы [ 2 ]

2 голосов
/ 28 февраля 2020

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

Когда мы говорим о транзакциях, мы также должны говорить об уровнях изоляции. Транзакции имеют значение для операторов чтения , так же, как и для всех других видов операций с базой данных. Уровень изоляции управляет тем, какие данные состояния согласованности считываются и устанавливаются ли блокировки чтения. В вашем примере уровень изоляции транзакции установлен на READ COMMITTED, что можно было сделать специально. Представьте, что для вашей базы данных по умолчанию установлен уровень транзакции READ UNCOMMITTED - использование неверного уровня изоляции может привести к нежелательным побочным эффектам, в зависимости от того, чего вы пытаетесь достичь с помощью этого запроса.

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

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

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

1) Используйте Required вместо RequiresNew. Один из методов в иерархии вызовов уже может иметь объявленную область транзакции. Если вы создадите вложенную область транзакции, используя RequiresNew, как это происходит в вашем случае, она не будет повторно использовать эту существующую транзакцию, а вместо этого создаст новую. Изменение параметра области действия на Required приведет к повторному использованию транзакции из родительской области или к созданию новой, если родительская область отсутствует.

2) Можно попытаться переместить объявление области несколько уровни в иерархии вызовов. TransactionScope является окружающим, поэтому область транзакции «наследуется» при последующих вызовах методов. Таким образом, вы можете уменьшить количество создаваемых объектов.

1 голос
/ 28 февраля 2020

РЕДАКТИРОВАТЬ: Этот ответ неправильный, но я оставляю его здесь, потому что я чему-то научился. @stuartd в комментариях:

Ошибка ... вам не нужно присваивать объявление использования переменной. dotnetfiddle.net / U0PLBw

ОРИГИНАЛЬНЫЙ НЕПРАВИЛЬНЫЙ ОТВЕТ: using должен гарантировать удаление нового объекта TransactionScope, что должно гарантировать, что он не будет вытекать. Однако, если присмотреться, новый TransactionScope не будет назначен какой-либо локальной переменной, и поэтому вероятный недоступен для вызова Dispose в конце блока using.

Я бы сказал, что вы правы, чтобы полностью удалить использование (так как TransactionScope все равно не используется - не может быть); также я бы сообщил об этом Microsoft с предложением, что такая конструкция должна генерировать предупреждение компилятора, если это еще не сделано.

...