EF4 - Как подключиться к существующей транзакции? - PullRequest
0 голосов
/ 30 ноября 2011

У меня есть веб-приложение, которое использует asp.net mvc, и я создаю свой контекст EF в базовом контроллере, и он доступен для всего запроса, а затем располагаю контекстом при удалении базового контроллера.Я хочу иметь возможность подключить соединение контекста в существующую транзакцию, которая была сохранена в сеансе.

Запустите новую транзакцию, используя:

ObjectContext context = new MyEntities(myConnectionString);
DbTransaction transaction = context.Connection.BeginTransaction();

, а затем сохраните ее в текущем сеансе:

HttpContext.Current.Session["EFTransaction"] = transaction;

И затем по новому запросу извлеките транзакцию изсеанс и подключите контекст:

context.Connection.EnlistTransaction(
(???)HttpContext.Current.Session["EFTransaction"]);

Единственная проблема в том, что я не знаю, к чему приводить (???).Метод BeginTransaction() возвращает объект System.Data.Common.DbTransaction, а для EnlistTransaction(System.Transactions.Transaction transaction) требуется объект Transaction.

Моя цель состоит в том, чтобы в основном создавать и изменять одни и те же объекты между запросами, а затем фиксировать все изменения в БД, когда пользовательготов их спасти.

Ответы [ 2 ]

2 голосов
/ 01 декабря 2011

Транзакция хранится в сеансе?OMG ...

Стоп! Хранение транзакции в сессии - ужасно неправильная идея.Вернитесь к своему анализу и измените архитектуру сейчас .

Почему?Предположим, что ваше приложение используется более чем одним пользователем (да, обычно это происходит для приложений ASP.NET).Теперь первый пользователь помечает изменение данных в вашей длительной транзакции.Транзакция действительно связывается с базой данных и использует блокировки для записей.Поэтому после первого незафиксированного изменения транзакция заблокировала некоторую запись.Теперь каждый запрос от другого пользователя / транзакции, который будет пытаться получить доступ к той же записи, будет ждать, пока транзакция модификации не будет завершена (если вы не разрешите чтение незафиксированных данных, что почти не отличается от использования транзакций вообще).Если транзакция модификации занимает 10 минут, все другие пользователи, имеющие доступ к той же записи, будут иметь тайм-аут.

Это всего лишь один пример, почему это совершенно неверная идея.Это, в частности, высокая вероятность возникновения «мертвых» блокировок, проблемы с производительностью на сервере SQL, тайм-ауты транзакций и т. Д.

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

Это делается совершенно другим способом:

Storeобъекты в сеансе и отправка их в базу данных, когда пользователь завершает все изменения - сам SaveChanges использует транзакцию, поэтому, если вы сохраняете изменения вместе, они будут в транзакции, и до тех пор, пока пользователь не завершит изменения, нет причин их сохранять.Имейте в виду, что это не означает сохранение контекста в сеансе!

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

1 голос
/ 01 декабря 2011

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

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

using (var tscope = new TransactionScope()
 {
  ..... all changes here ...
  tscope.Complete()
 }

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

если вас беспокоят проблемы параллелизма, вы можете использовать столбцы на основе меток времени в своих таблицах.

также позвольте мне добавить, что EF неявно использует транзакцию при сохранениивызывается, внешняя область необходима только тогда, когда требуется несколько сохранений или когда уровень транзакции по умолчанию (READ COMMITTED на Sql Server) недостаточен.

...