Можно ли отключить привязку к внешним транзакциям в Entity Framework Core 8 глобально?

Краткая версия

Мы хотим отключить автоматическое присоединение к внешним транзакциям (System.Transactions.TransactionScope) для всей Entity Framework Core 8 DbContext. DbContext ни в коем случае не должен быть включен в уже действующую внешнюю транзакцию.

Мы не хотим явно создавать новый TransactionScope с TransactionScopeOption.Suppress везде в коде, где мы обращаемся к DbContext, и поэтому мы хотели бы отключить его глобально. Мы хотим абстрагировать эти детали реализации и уменьшить сложность кода. Разработчиков не должно волновать это при доступе к DbContext.

Более длинная версия с большим контекстом

У нас есть два разных ядра Entity Framework Core, которые подключаются к двум разным базам данных. Одна из двух баз данных используется только для чтения, и поэтому нам не нужны (распределенные) транзакции по обеим базам данных. Для этой базы данных мы хотели бы отключить привязку к внешним транзакциям.

У нас есть шаблон декоратора на месте создания DbContext. Внутри декорируемого объекта можно использовать обе базы данных, и по умолчанию обе TransactionScope подключаются к внешней транзакции. Мы хотим отключить регистрацию базы данных, доступной только для чтения.

Что мы пробовали

  1. Для ADO.NET это возможно, указав свойство DbContext в строке подключения. Но, похоже, это не работает для платформы сущностей, поскольку EF не учитывает это свойство строки подключения.

  2. Мы попытались создать Enlist=false и переопределить метод DbCommandInterceptor и установить транзакцию соединения на ReaderExecuting:

    public override InterceptionResult<DbDataReader> ReaderExecuting(
        DbCommand command,
        CommandEventData eventData,
        InterceptionResult<DbDataReader> result)
    {
        command.Connection?.EnlistTransaction(null);
        return base.ReaderExecuting(command, eventData, result);
    }
    

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

  3. Мы рассматривали возможность создания нового null с TransactionScope внутри перехватчика. Но мы не уверены, как правильно справиться с размещением TransactionScopeOption.Suppress внутри перехватчика. Это также кажется хакерским, и мы не хотим приводить к утечкам ресурсов из-за неправильной реализации.

Возможно (просто вопрос), вам следует пересмотреть, нужна ли вам эта область транзакций и зачем вам это нужно. Я видел множество случаев, когда транзакции/области транзакций запускались, но при более внимательном рассмотрении оказалось, что код можно переписать так, чтобы он выполнял только один вызов SaveChanges.

Gert Arnold 26.08.2024 14:50

Спасибо @GertArnold. К сожалению, полное удаление TransactionScope в этом случае невозможно. Мы полагаемся на него, поскольку у нас есть и другой код, который подключается к нему (вне структуры сущности).

M.E. 26.08.2024 15:02
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Благодаря подсказкам в комментариях @Святослава Данилива, у меня все получилось, выполнив следующие шаги:

  1. Создайте класс, производный от SqlServerConnection, чтобы отключить внешние транзакции на уровне Entity Framework:

    internal class NoAmbientTransactionSqlServerConnection(
      RelationalConnectionDependencies dependencies)
        : SqlServerConnection(dependencies)
    {
        protected override bool SupportsAmbientTransactions => false;
    }
    
  2. При регистрации DbContext замените IRelationalConnection этой пользовательской реализацией, используя DbContextOptionsBuilder:

    options.ReplaceService<IRelationalConnection, NoAmbientTransactionSqlServerConnection>();
    
  3. Этого все еще недостаточно, поскольку базовый класс SqlServerConnection внутренне использует DbConnection из клиента SQL. Это DbConnection все равно будет включено во внешнюю транзакцию. Поэтому добавьте Enlist=false в строку подключения, чтобы отключить привязку и на уровне клиента SQL.

Небольшим недостатком является то, что SqlServerConnection — это внутренний API Entity Framework Core, который выдает предупреждение при его использовании, и его следует использовать с осторожностью, и его можно изменить или удалить без предварительного уведомления.

Другие вопросы по теме