OptimisticConcurrency Исключение, когда без изменений

Я иногда получаю OptimisticConcurrency Исключения при звонке SaveChanges() используя Entity Framework 6 в веб-приложении MVC, хотя я не вносил изменений ни в какие объекты в этом потоке выполнения. Конечно, возможно, что изменения были сделаны в другом потоке / процессах. Это кажется невозможным для OptimisticConcurrencyИсключение не может произойти, но, увы, это происходит с некоторой частотой.

var launchedSurvey = DB.Find<LaunchedSurvey>(id);
DB.SaveChanges(); // throws OptimisticConcurrencyException 

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

Это сообщение об ошибке

System.Exception: TestSave Failed ---> System.Data.Entity.Infrastructure.DbUpdateException: SaveChangesAndReload caught exception and attempted to handle it => attempts:5
 ---> System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details. ---> System.Data.Entity.Core.OptimisticConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
   at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.ValidateRowsAffected(Int64 rowsAffected, UpdateCommand source)
   at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions options, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction)
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
   at System.Data.Entity.Internal.InternalContext.SaveChanges()

Вот c-tor для LaunchedSurvey

public LaunchedSurvey()
{
    CreationTime = DateTime.UtcNow;
    WorkerLaunchedSurveys = new Collection<WorkerLaunchedSurvey>();
    WorkerQualificationRequests = new Collection<WorkerQualificationRequest>();
    _remainingDemographic = new DateTime(1900, 1, 1);
    MinimumPercent = 0.10;
    ExcludeList = new List<LaunchedSurvey>();
    IncludeList = new List<LaunchedSurvey>();
    ScreenedWorkers = new List<ScreenedWorker>();
    EmailMessages = new List<EmailMessage>();
    AssignmentsPendingWorkers = new HashSet<AssignmentsPendingWorker>();
    ExcludedWorkersManual = new List<AmazonWorkerExcluded>();
    IncludedWorkersManual = new List<AmazonWorkerIncluded>();
    PendingWorkersQualificationsUnknown = new List<AmazonWorkerPending>();
    Tags = new List<Tag>();
    ManualQualificationTypeIdList = new List<QualificationType>();
    WorkerSurveyStatisticAuto = new WorkerSurveyStatisticAuto();
    WorkerSurveyStatisticManual = new WorkerSurveyStatisticManual();
    WorkerUniqueIdentifierList = new List<WorkerUniqueIdentifier>();
}

Любое понимание этого очень ценится!

1 ответ

Решение

В ваших двух строках кода...

var launchedSurvey = DB.Find<LaunchedSurvey>(id);
DB.SaveChanges(); 

... единственный исполняемый код приложения - это конструктор и код в установщиках свойств. Вот почему я попросил посмотреть конструктор (код в установщиках свойств встречается реже).

Конечно, я не знаю, какие свойства сопоставлены, но конструктор показывает два возможных хранимых назначения:

WorkerSurveyStatisticAuto = new WorkerSurveyStatisticAuto();
WorkerSurveyStatisticManual = new WorkerSurveyStatisticManual();

Если эти ссылки сопоставлены, т.е. если связанные объекты являются сопоставленными объектами, EF не установит их снова, когда LaunchedSurvey Объект материализован. Теперь, когда вы сохраняете объект, EF попытается присвоить 0 значениям внешнего ключа. Это может либо -

  • всегда генерировать исключение ограничения FK, потому что я не ожидаю, что записи с Id = 0 будут присутствовать в базе данных
  • выбросить исключения проверки, потому что новые объекты не находятся в допустимом состоянии.
  • генерировать исключение оптимистичного параллелизма, если какой-либо из упомянутых объектов был обновлен одновременно действующим пользователем за короткий промежуток времени между извлечением и сохранением LaunchedSurvey объект.

Сообщение: не устанавливать сопоставленные ссылочные свойства в конструкторах сущностей. Смотрите также: EF codefirst: я должен инициализировать свойства навигации?

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

В моем случае я вставлял повторяющуюся строку в таблицу с IGNORE_DUB_KEY=ON. Это заставляет SQL-сервер возвращать "0 затронутых строк" ​​вместе с предупреждением о нарушениях ограничения первичного ключа, а не выдавать ошибку.

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