I am occasionally receiving OptimisticConcurrency
Exceptions when calling SaveChanges()
using Entity Framework 6 within an MVC web application even though I made no change to any objects within this thread of execution. It is certainly possible that changes were made within a different thread/processs. It seems to be impossible for an OptimisticConcurrency
exception to occur but, alas, it occurs with some frequency.
var launchedSurvey = DB.Find<LaunchedSurvey>(id);
DB.SaveChanges(); // throws OptimisticConcurrencyException
I call SaveChanges
even though no changes occurred simply as a test since I was receiving the same exception when records were added but no changes to existing objects occurred..which also seems should not throw an OptimisticConcurrencyException
.
This is the error message
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()
Here is the c-tor for 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>();
}
Any insight into this is greatly appreciated!
In your two lines of code...
var launchedSurvey = DB.Find<LaunchedSurvey>(id);
DB.SaveChanges();
...the only application code that runs is the constructor and code in property setters. That's why I asked to see the constructor (code in property setters is less common).
Of course I don't know which properties are mapped, but the constructor reveals two possibly storable assignments:
WorkerSurveyStatisticAuto = new WorkerSurveyStatisticAuto();
WorkerSurveyStatisticManual = new WorkerSurveyStatisticManual();
If these references are mapped, i.e if the involved objects are mapped entities, EF won't set them again when the LaunchedSurvey
object is materialized. Now when you save the object, EF will try to assign 0 to the foreign key values. This may either -
LaunchedSurvey
object.The message is: don't set mapped reference properties in entity constructors. See also: EF codefirst : Should I initialize navigation properties?