1289 / 868 / 258
Регистрация: 08.08.2014
Сообщений: 2,482
|
|
1 | |
Управление транзакциями18.04.2019, 22:10. Показов 3067. Ответов 4
Метки нет (Все метки)
Вопрос больше по архитектуре, чем по конкретной реализации.
Есть какие-то проверенные практики по управлению транзакциями в рамках определённого контекста выполнения (например, в пределах одного API-запроса)? К тому, что ниже - я в курсе, что транзакции могут быть не только поверх конкретной базы под ORM, что они могут покрывать сразу несколько БД, могут включать в себя обращения к другим сервисам или очередям. Тут пока интересует самый простой случай, дабы совсем не запутаться. Например, для стандартного сценария прохождения запроса на API: 1. Контроллер. 2. Сервис БЛ <==> Сервис БЛ <==> Сервис БЛ. 3. Репозиторий-1, Репозиторий-2. 4. ORM. Ключевой пункт [2], где сервисы могут переиспользовать друг-друга в неожиданном порядке (т.е. они с точки зрения архитектуры все на одном уровне) и каждому из них может требоваться или не требоваться транзакция при общении с [3]->[4]. Из этого сходу напрашивается очевидное решение - регистрировать репозиторий/контекст-ORM со временем жизни 'Scoped', где-то до [2] заранее всегда открывать транзакцию при прохождении запроса вперёд и закрывать/откатывать при возврате результата. Например, в Invoke мидлвара, непосредственно перед вызовом метода контроллера.Это удобное решение с точки зрения "один раз сделал и забыл", но, имхо, с точки зрения архитектуры кривоватое, т.к. сервисы теряют контроль над частью того функционала, который относится к их области ответственности (их ведь, да?). Т.е. если этот сервис вдруг будет переиспользован в другом API, то тот API тоже будет вынужден знать об особенностях применения транзакций этими сервисами. Другая мысль - сервис БЛ сам открывает и закрывает транзакции, если ему это необходимо. Однако, в этом случае, если этот сервис вызывает какой-то другой сервис и ему тоже нужна транзакция, то нужно чтобы оба они работали в рамках одной транзакции, и появляется необходимость делать костыль с подсчётом количества открытий/закрытий транзакций и игнорированием лишних открытий и непоследних закрытий. Т.к. если транзакция жива в рамках всего запроса (или цепочки методов сервисов), то повторное её открытие приведёт к ошибке, так же как и преждевременное закрытие.
0
|
18.04.2019, 22:10 | |
Ответы с готовыми решениями:
4
Управление транзакциями Управление транзакциями в С++ Работа с транзакциями Проблемы с распределёнными транзакциями |
12567 / 8737 / 1311
Регистрация: 21.01.2016
Сообщений: 32,767
|
|
20.04.2019, 10:01 | 2 |
Сообщение было отмечено kotelok как решение
Решение
Именно такое решение.
А вот это уже фигня) Используйте класс TransactionScope . Он допускает вложенные транзакции. Если сервису хочется открыть транзакцию, то он может это сделать со спокойной совестью. Ему не нужно знать о том, что транзакцию уже открыли. Провайдер ADO.NET тоже знает о TransactionScope (как и все ORM) и о возможности вложения транзакций.Всё придумано до вас.
1
|
1289 / 868 / 258
Регистрация: 08.08.2014
Сообщений: 2,482
|
|
20.04.2019, 12:59 [ТС] | 3 |
Пока не получилось разобраться, как прозрачно встроить его в проект.
Сейчас сделано на простом костыле - LinqToDB.Data.DataConnection регистрируется со временем жизни Scope (т.е. в рамках одного API-запроса), при создании этого объекта он автоматически открывает подключение к БД и удерживает его (это не я, он сам так себя ведёт).Ну и в реализации DataConnection просто переопределил методы Begin/Commit/Rollback и считаю там количество открытий/закрытий, как описал выше.Правда, это порождает некоторые неоднозначные ситуации и проблемы: 1. Транзакция открывается на уровне объекта ORM, сервис БЛ к этому объекту доступа не имеет, приходится в репозиториях (наследованием от базового репозитория) выставлять наружу свои методы Begin/Commit/Rollback . Кривовато выглядит.2. Первый сервис открыл транзакцию, вложенный сервис тоже (по факту ничего не произошло), потом вложенный сервис откатил транзакцию (по факту ничего не произошло), а первый сервис её закоммитил. В итоге изменения вложенного сервиса тоже закоммитились. С другой стороны, это весьма странная ситуация с точки зрения БЛ, т.е. по идее, если вложенный сервис не смог отработать, он должен кинуть исключение и верхний сервис тоже откатит транзакцию. Добавлено через 34 минуты Как-то оно странно работает: 1. Сервис-1 создаёт TransactionScope (внутри using). Добавляет запись в таблицу. Вызывает метод Сервис-2.2. Сервис-2 создаёт TransactionScope (внутри using). Добавляет запись в таблицу. Делает Complete .3. Сервис-1 получает управление обратно. Далее (для теста) генерирует исключение. Т.е. он не доходит до вызова Complete .4. Результат: обе записи добавлены в таблицу. Добавлено через 9 минут Не подскажете, как правильно использовать TransactionScope в рамках описанной в первом посте задачи и в сочетании с LinqToDb ?Пока что, без TransactionScope , получилось проапгрейдить реализованный выше велосипед до отдельного сервиса с временем жизни Scoped , который имеет методы Begin/Commit/Rollback и знает про все коннекты уровня сессии. Это позволило убрать работу с транзакциями на уровне репозиториев. Теперь, если сервису БЛ нужна транзакция, он обращается к сервису транзакций, а тот уже разруливает вложенность обращений. Дополнительное удобство - т.к. нет необходимости оборачивать запуск транзакции в блок using , методы БЛ выглядят аккуратнее (без лишних отступов).
0
|
12567 / 8737 / 1311
Регистрация: 21.01.2016
Сообщений: 32,767
|
||||||
20.04.2019, 13:27 | 4 | |||||
Вы в этом уверены? Подключение должно открываться только на время выполнения запроса и после его выполнения - закрываться. Добавлено через 1 минуту Пфффффф. Что тут ещё можно сказать...
1
|
1289 / 868 / 258
Регистрация: 08.08.2014
Сообщений: 2,482
|
|||||||||||||||||||||
20.04.2019, 15:56 [ТС] | 5 | ||||||||||||||||||||
----
C проблемой разобрался, "простыня" новых вопросов не содержит, так что её можно не читать. Решение в последнем абзаце. ---- контекст БД
регистрация
Когда какой-то сервис запрашивает MainDb , прилетает вызов в лябмду (где регистрация), создаю объект, попадаю в конструктор MainDb и в этот момент свойство Connection инициализировано и находится в состоянии Open . Хотя, вероятно, оно самоинициализируется и открывается при попытке просмотра его в отладке (наверное, можно как-то через рефлекшн это проверить).Добавлено через 58 секунд Usaga
Я выше написал - если внутри вложенного сервиса создаю ещё один TransactionScope , там его коммичу, а в сервисе уровнем выше падаю с исключением до Complete , то в базу коммитятся все изменения - и из внутреннего сервиса, которой закоммитился, и из вышестоящего, который упал.Добавлено через 7 минут Да, проверил через reflection, подключение и в самом деле 'null' до первого использования. Инициализируется при просмотре через окно отладки. Добавлено через 15 минут И тогда я не понимаю, если LinqToDb каждый раз при выполнении запроса открывает/закрывает соединение, то как TransactionScope убедит его держать одно открытое соединение с активной транзакцией при межсервисных вызовах?Добавлено через 15 минут Не убеждает ). Вот этот вызов добавляет запись в БД, хотя tr.Complete() нигде не вызывается:
Всё, разобрался. В строке подключения к БД требуется явно задать параметр: Enlist=True; . После этого TransactionScope начинает работать корректно. По крайней мере под Виндой. Надо будет ещё пол Дебианом проверить.
0
|
20.04.2019, 15:56 | |
20.04.2019, 15:56 | |
Помогаю со студенческими работами здесь
5
Разобраться с транзакциями MySQL+dbExpress Запуск внешних приложений из 1С:Предприятия и работа с транзакциями Реализовать алгоритм работы планировщика. Управление виртуальной памятью. Управление файловой системой Движение, вращение, управление движением, управление вращением фигуры Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи | |||||
Книги и учебные ресурсы по C#
InfoMaster 08.01.2025
Базовые учебники и руководства
Одной из лучших книг для начинающих является "C# 10 и . NET 6 для начинающих" Эндрю Троелсена и Филиппа Джепикса . Книга последовательно раскрывает основные концепции. . .
|
Что такое NullReferenceException и как исправить?
InfoMaster 08.01.2025
NullReferenceException - одно из самых распространенных исключений, с которым сталкиваются разработчики на C#. Это исключение возникает при попытке обратиться к членам объекта (методам, свойствам или. . .
|
Что такое Null Pointer Exception (NPE) и как это исправить?
InfoMaster 08.01.2025
Null Pointer Exception (NPE) - это одно из самых распространенных исключений в Java, которое возникает при попытке использовать ссылку на объект, значение которой равно null. Это исключение относится. . .
|
Русский язык в консоли C++
InfoMaster 08.01.2025
При разработке программ на C++ одной из частых проблем, с которой сталкиваются русскоязычные программисты, является корректное отображение кириллицы в консольных приложениях. Эта проблема особенно. . .
|
Telegram бот на C#
InfoMaster 08.01.2025
Разработка ботов для Telegram стала неотъемлемой частью современной экосистемы мессенджеров. C# предоставляет мощный и удобный инструментарий для создания разнообразных ботов, от простых. . .
|
Использование GraphQL в Go (Golang)
InfoMaster 08.01.2025
Go (Golang) является одним из наиболее популярных языков программирования, используемых для создания высокопроизводительных серверных приложений. Его архитектурные особенности и встроенные. . .
|
Что лучше использовать при создании класса в Java: сеттеры или конструктор?
Alexander-7 08.01.2025
Вопрос подробнее:
На вопрос: «Когда одновременно создаются конструктор и сеттеры в классе – это нормально?» куратор уточнил: «Ваш класс может вообще не иметь сеттеров, а только конструктор и геттеры. . .
|
Как работать с GraphQL на TypeScript
InfoMaster 08.01.2025
Введение в GraphQL и TypeScript
В современной разработке веб-приложений GraphQL стал мощным инструментом для создания гибких и эффективных API. В сочетании с TypeScript, эта технология. . .
|
Счётчик на базе сумматоров + регистров и генератора сигналов согласования.
Hrethgir 07.01.2025
Создан с целью проверки скорости асинхронной логики: ранее описанного сумматора и предополагаемых fast регистров. Регистры созданы на базе ранее описанного, предполагаемого fast триггера. То-есть. . .
|
Как перейти с Options API на Composition API в Vue.js
BasicMan 06.01.2025
Почему переход на Composition API актуален
В мире современной веб-разработки фреймворк Vue. js продолжает эволюционировать, предлагая разработчикам все более совершенные инструменты для создания. . .
|
Архитектура современных процессоров
inter-admin 06.01.2025
Процессор (центральный процессор, ЦП) является основным вычислительным устройством компьютера, которое выполняет обработку данных и управляет работой всех остальных компонентов системы. Архитектура. . .
|
История создания реляционной модели баз данных, правила Кодда
Programming 06.01.2025
Предпосылки создания реляционной модели
В конце 1960-х годов компьютерная индустрия столкнулась с серьезными проблемами в области управления данными. Существовавшие на тот момент модели данных -. . .
|