В настоящее время я получаю эту ошибку:
System.Data.SqlClient.SqlException: новая транзакция не разрешена, поскольку в сеансе запущены другие потоки.
во время выполнения этого кода:
public class ProductManager : IProductManager
{
#region Declare Models
private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
private RivWorks.Model.NegotiationAutos.RivFeedsEntities _dbFeed = RivWorks.Model.Stores.FeedEntities(AppSettings.FeedAutosEntities_connString);
#endregion
public IProduct GetProductById(Guid productId)
{
// Do a quick sync of the feeds...
SyncFeeds();
...
// get a product...
...
return product;
}
private void SyncFeeds()
{
bool found = false;
string feedSource = "AUTO";
switch (feedSource) // companyFeedDetail.FeedSourceTable.ToUpper())
{
case "AUTO":
var clientList = from a in _dbFeed.Client.Include("Auto") select a;
foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
{
var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
{
if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
{
var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
{
foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
{
if (targetProduct.alternateProductID == sourceProduct.AutoID)
{
found = true;
break;
}
}
if (!found)
{
var newProduct = new RivWorks.Model.Negotiation.Product();
newProduct.alternateProductID = sourceProduct.AutoID;
newProduct.isFromFeed = true;
newProduct.isDeleted = false;
newProduct.SKU = sourceProduct.StockNumber;
company.Product.Add(newProduct);
}
}
_dbRiv.SaveChanges(); // ### THIS BREAKS ### //
}
}
}
break;
}
}
}
Модель № 1 - Эта модель находится в базе данных на нашем Dev-сервере. Модель № 1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png
Модель № 2 - эта модель находится в базе данных на нашем сервере Prod и обновляется каждый день автоматически. альтернативный текст http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png
Примечание. Элементы в красном кружке в модели № 1 - это поля, которые я использую для «сопоставления» с моделью № 2. Пожалуйста, не обращайте внимания на красные кружки в Модели № 2: это из другого моего вопроса, на который сейчас дан ответ.
Примечание: мне все еще нужно поставить проверку isDeleted, чтобы я мог мягко удалить ее из DB1, если она вышла из инвентаря нашего клиента.
Все, что я хочу сделать с этим конкретным кодом, - это связать компанию в DB1 с клиентом в DB2, получить их список продуктов от DB2 и вставить его в DB1, если его там еще нет. Первый раз должен быть полный запас инвентаря. Каждый раз, когда он запускается там, после того, как ничего не должно произойти, если за ночь не появилось новое количество материала.
Таким образом, большой вопрос - как решить ошибку транзакции, которую я получаю? Нужно ли мне каждый раз удалять и пересоздавать свой контекст через циклы (для меня это не имеет смысла)?
источник
Ответы:
После долгих выдергиваний из волос я обнаружил, что
foreach
петли были виновниками. Что должно произойти, это вызвать EF, но вернуть его вIList<T>
этот целевой тип, а затем зациклить наIList<T>
.Пример:
источник
SaveChanges
пока вы все еще извлекаете результаты из БД. Поэтому другое решение - просто сохранить изменения после завершения цикла.Как вы уже определили, вы не можете сохранить изнутри объект,
foreach
который все еще рисует из базы данных через активного считывателя.Вызов
ToList()
илиToArray()
подходит для небольших наборов данных, но если у вас есть тысячи строк, вы будете использовать большой объем памяти.Лучше загружать строки кусками.
Учитывая вышеописанные методы расширения, вы можете написать свой запрос следующим образом:
Запрашиваемый объект, для которого вы вызываете этот метод, должен быть упорядочен. Это связано с тем, что Entity Framework поддерживает только
IQueryable<T>.Skip(int)
упорядоченные запросы, что имеет смысл, если учесть, что несколько запросов для разных диапазонов требуют, чтобы упорядоченность была стабильной. Если порядок не важен для вас, просто упорядочите по первичному ключу, поскольку он может иметь кластерный индекс.Эта версия будет запрашивать базу данных партиями по 100. Обратите внимание, что
SaveChanges()
вызывается для каждой сущности.Если вы хотите значительно повысить пропускную способность, вам следует звонить
SaveChanges()
реже. Вместо этого используйте такой код:Это приводит к уменьшению количества обращений к базе данных в 100 раз. Конечно, каждый из этих звонков длится дольше, но в итоге вы все равно выходите далеко вперед. Ваш пробег может варьироваться, но это было миры быстрее для меня.
И это обходит исключение, которое вы видели.
РЕДАКТИРОВАТЬ Я вернулся к этому вопросу после запуска SQL Profiler и обновил несколько вещей для повышения производительности. Для тех, кто заинтересован, вот пример SQL, который показывает, что создано БД.
Первый цикл не должен ничего пропускать, поэтому он проще.
Последующие вызовы должны пропускать предыдущие фрагменты результатов, поэтому вводит использование
row_number
:источник
Теперь мы опубликовали официальный ответ на ошибку, открытую в Connect . Мы рекомендуем следующие обходные пути:
Эта ошибка связана с тем, что Entity Framework создает неявную транзакцию во время вызова SaveChanges (). Лучший способ обойти ошибку - использовать другой шаблон (т.е. не сохранять в процессе чтения) или явно объявить транзакцию. Вот три возможных решения:
источник
Действительно, вы не можете сохранить изменения внутри
foreach
цикла в C #, используя Entity Framework.context.SaveChanges()
Метод действует как коммит в обычной системе баз данных (RDMS).Просто внесите все изменения (которые Entity Framework будет кэшировать), а затем сохраните их все сразу, вызывая
SaveChanges()
после цикла (вне его), как команда коммита базы данных.Это работает, если вы можете сохранить все изменения сразу.
источник
Просто поставьте
context.SaveChanges()
после окончания вашегоforeach
(цикл).источник
Всегда используйте ваш выбор в качестве списка
Например:
Затем переберите коллекцию, сохраняя изменения
источник
К вашему сведению: из книги и некоторых строк откорректировано, потому что все еще действует:
Вызов метода SaveChanges () начинает транзакцию, которая автоматически откатывает все сохраненные в базе данных изменения, если возникает исключение до завершения итерации; в противном случае транзакция совершается. Вы можете испытать желание применить метод после каждого обновления или удаления сущности, а не после завершения итерации, особенно когда вы обновляете или удаляете огромное количество сущностей.
Если вы попытаетесь вызвать SaveChanges () до того, как все данные будут обработаны, вы получите исключение «Новая транзакция запрещена, поскольку в сеансе запущены другие потоки». Исключение возникает из-за того, что SQL Server не разрешает запуск новой транзакции в соединении, в котором открыт SqlDataReader, даже с включением нескольких активных наборов записей (MARS) в строке подключения (строка подключения EF по умолчанию включает MARS)
Иногда лучше понять, почему что-то происходит ;-)
источник
Создайте свои списки для запросов в .ToList (), и он должен работать нормально.
источник
Я получал эту же проблему, но в другой ситуации. У меня был список предметов в списке. Пользователь может щелкнуть элемент и выбрать «Удалить», но я использую сохраненный процесс для удаления элемента, поскольку при удалении элемента используется много логики. Когда я вызываю сохраненный процесс, удаление работает нормально, но любой будущий вызов SaveChanges вызовет ошибку. Моим решением было вызвать хранимый процесс за пределами EF, и это работало нормально. По какой-то причине, когда я вызываю хранимый процесс, используя способ EF, он оставляет что-то открытым.
источник
SELECT
инструкция в хранимой процедуре, которая выдает пустой набор результатов, и если этот набор результатов не был прочитан,SaveChanges
выдается это исключение.Вот еще 2 параметра, которые позволяют вам вызывать SaveChanges () в каждом цикле.
Первый вариант - использовать один DBContext, чтобы сгенерировать список объектов для перебора, а затем создать второй DBContext для вызова SaveChanges (). Вот пример:
Второй вариант - получить список объектов базы данных из DBContext, но выбрать только идентификаторы. Затем выполните итерацию по списку идентификаторов (предположительно, int), получите объект, соответствующий каждому int, и таким образом вызовите SaveChanges (). Идея этого метода заключается в получении большого списка целых чисел, гораздо более эффективном, чем получение большого списка объектов БД и вызов .ToList () для всего объекта. Вот пример этого метода:
источник
Если вы получаете эту ошибку из-за foreach, и вам действительно нужно сначала сохранить один объект внутри цикла и использовать сгенерированный идентификатор в цикле, как это было в моем случае, самое простое решение - использовать другой DBContext для вставки объекта, который будет возвращать Id и использовать этот идентификатор во внешнем контексте
Например
источник
Таким образом, в проекте, где у меня была точно такая же проблема, проблема была не в том,
foreach
или.toList()
она была в конфигурации AutoFac, которую мы использовали. Это создало некоторые странные ситуации, в которых была выдана вышеупомянутая ошибка, но также была выброшена куча других эквивалентных ошибок.Это было наше исправление: Изменено это:
Для того, чтобы:
источник
Я знаю, что это старый вопрос, но я столкнулся с этой ошибкой сегодня.
и я обнаружил, что эта ошибка может быть выдана, когда триггер таблицы базы данных получает ошибку.
для вашей информации, вы можете проверить триггеры таблиц, когда вы получите эту ошибку.
источник
Мне нужно было прочитать огромный ResultSet и обновить некоторые записи в таблице. Я пытался использовать куски , как предложено в Drew Нокс «ы ответ .
К сожалению, после 50000 записей я получил исключение OutofMemoryException. В ответе большого набора данных Entity Framework, исключение из памяти объясняет, что
Рекомендуется заново создать контекст для каждого пакета.
Итак, я извлек минимальные и максимальные значения первичного ключа - у таблиц есть первичные ключи как автоинкрементные целые числа. Затем я извлек из базы данных фрагменты записей, открыв контекст для каждого фрагмента. После обработки контекст чанка закрывается и освобождает память. Это гарантирует, что использование памяти не растет.
Ниже приведен фрагмент из моего кода:
FromToRange - это простая структура со свойствами From и To.
источник
Мы начали видеть эту ошибку «Новая транзакция не разрешена, потому что в сеансе запущены другие потоки» после миграции с EF5 на EF6.
Google привел нас сюда, но мы не звоним
SaveChanges()
в цикле. Ошибки возникали при выполнении хранимой процедуры с использованием ObjectContext.ExecuteFunction внутри цикла foreach, считывающего из БД.Любой вызов ObjectContext.ExecuteFunction оборачивает функцию в транзакцию. Начало транзакции, когда уже открыт читатель, вызывает ошибку.
Можно отключить упаковку SP в транзакции, установив следующую опцию.
Эта
EnsureTransactionsForFunctionsAndCommands
опция позволяет SP работать без создания собственной транзакции, и ошибка больше не возникает.Свойство DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands
источник
Я также столкнулся с той же проблемой.
Вот причина и решение.
http://blogs.msdn.com/b/cbiyikoglu/archive/2006/11/21/mars-transactions-and-sql-error-3997-3988-or-3983.aspx
Убедитесь, что перед запуском команд обработки данных, таких как вставки, обновления, вы закрыли все предыдущие активные программы чтения SQL.
Наиболее распространенная ошибка - это функции, которые читают данные из БД и возвращают значения. Например, для таких функций, как isRecordExist.
В этом случае мы немедленно возвращаемся из функции, если нашли запись и забываем закрыть читатель.
источник
Код ниже работает для меня:
источник
В моем случае проблема возникла, когда я вызвал хранимую процедуру через EF, а затем SaveChanges выдает это исключение. Проблема была в вызове процедуры, счетчик не был утилизирован. Я исправил код следующим образом:
источник
Я сильно опаздываю на вечеринку, но сегодня я столкнулся с той же ошибкой, и решение было простым. Мой сценарий был похож на данный код, я выполнял транзакции БД внутри вложенных циклов for-each.
Проблема заключается в том, что транзакция с одной БД занимает немного больше времени, чем цикл for-each, поэтому, как только более ранняя транзакция не завершена, новая тяга вызывает исключение, поэтому решение заключается в создании нового объекта в цикле for-each где вы делаете транзакцию БД.
Для вышеупомянутых сценариев решение будет таким:
источник
Я немного опоздал, но у меня тоже была эта ошибка. Я решил проблему, проверив, что, где значения, которые, где обновление.
Я обнаружил, что мой запрос был неправильным и что там более 250+ правок. Поэтому я исправил свой запрос, и теперь он работает правильно.
Надеюсь, это поможет решить будущие проблемы.
источник