为什么我的 WPF 应用程序有时会发生 Entity Framework 崩溃?
Why is this Entity Framework crash sometimes happening in my WPF app?
我管理一个公开可用的 WPF 应用程序,它使用 Entity Framework 6.1(模型优先的 ObjectContext)和一个 SQLite 数据库。
我的应用程序的第一个崩溃日志(根据 HockeyApp)如下(指向我的应用程序代码的指针标记为 ****):
System.InvalidOperationException: The property 'Id' is part of the object's key information and cannot be modified.
at System.Data.Entity.Core.Objects.EntityEntry.VerifyEntityValueIsEditable(StateManagerTypeMetadata typeMetadata, Int32 ordinal, String memberName)
at System.Data.Entity.Core.Objects.EntityEntry.GetAndValidateChangeMemberInfo(String entityMemberName, Object complexObject, String complexObjectMemberName, StateManagerTypeMetadata& typeMetadata, String& changingMemberName, Object& changingObject)
at System.Data.Entity.Core.Objects.EntityEntry.EntityMemberChanging(String entityMemberName, Object complexObject, String complexObjectMemberName)
at System.Data.Entity.Core.Objects.EntityEntry.EntityMemberChanging(String entityMemberName)
at System.Data.Entity.Core.Objects.ObjectStateEntry.System.Data.Entity.Core.Objects.DataClasses.IEntityChangeTracker.EntityMemberChanging(String entityMemberName)
at System.Data.Entity.Core.Objects.DataClasses.EntityObject.ReportPropertyChanging(String property)
****
at MyNamespace.MyApp.MyEntity.set_Id(Int64 value) in C:\MyPath\MyApp\EfGeneratedEntities.cs
****
at lambda_method(Closure , Object , Object )
at System.Data.Entity.Core.Objects.DelegateFactory.SetValue(EdmProperty property, Object target, Object value)
at System.Data.Entity.Core.Objects.StateManagerMemberMetadata.SetValue(Object userObject, Object value)
at System.Data.Entity.Core.Objects.Internal.LightweightEntityWrapper`1.SetCurrentValue(EntityEntry entry, StateManagerMemberMetadata member, Int32 ordinal, Object target, Object value)
at System.Data.Entity.Core.Objects.EntityEntry.SetCurrentEntityValue(StateManagerTypeMetadata metadata, Int32 ordinal, Object userObject, Object newValue)
at System.Data.Entity.Core.Objects.ObjectStateEntryDbUpdatableDataRecord.SetRecordValue(Int32 ordinal, Object value)
at System.Data.Entity.Core.Mapping.Update.Internal.PropagatorResult.SetServerGenValue(Object value)
at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.BackPropagateServerGen(List`1 generatedValues)
at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.<Update>b__2(UpdateTranslator ut)
at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update[T](T noChangesResult, Func`2 updateFunction)
at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update()
at System.Data.Entity.Core.Objects.ObjectContext.<SaveChangesToStore>b__d()
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.Core.Objects.ObjectContext.<>c__DisplayClassb.<SaveChangesInternal>b__8()
at System.Data.Entity.Infrastructure.DefaultExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
at System.Data.Entity.Core.Objects.ObjectContext.SaveChanges()
****
at MyNamespace.MyApp.MyContext.OnMyEvent() in C:\MyPath\MyApp\MyContext.cs
****
我无法重现此崩溃,因此无法尝试修复它。我很乐意对其进行排序。
我了解崩溃信息。但是我的应用程序没有做任何应该导致这种情况的事情。 MyEntity.Id
属性 是实体键(StoreGeneratedPattern
= Identity
),我从不手动设置 Id
值。我从不手动将实体标记为已修改。我的 SQLite 数据库没有任何触发器或任何其他可能会干扰 EF 对数据库生成的标识值的解释的东西。我真的看不出这种崩溃是怎么发生的,我自己也做不到 - 新实体的插入对我来说工作得很好。
我当然不指望任何人阅读这篇文章 post 并提出解决方案,但我希望有人能引导我朝着正确的方向前进。
哪些不明显的原因可能导致此崩溃发生?
我确定我会被要求提供源代码,但实际上并不多。在罪魁祸首代码中,我只是创建了一个 MyEntity
对象,设置了一些 属性 值,将其添加到上下文中,然后调用 SaveChanges()
。而已。正如我所说,它在我的测试和 99% 的客户使用中都运行良好。但是我每天都会收到好几份这样的崩溃报告,我很想结束它们。
----更新----
我认为值得指出的是,堆栈跟踪中的 MyEntity.set_Id(Int64 value)
显然是 Entity Framework 的 key/identity 值检索的一部分(参见 PropagatorResult.SetServerGenValue(Object value)
周围的行, ETC。)。这不是 my 设置 Id
的代码,而是 EF.
查看 Entity Framework source code,尝试为不处于 Added
状态的实体设置实体键值时抛出此异常:
// Key fields are only editable if the entry is the `Added` state.
if (member.IsPartOfKey
&& State != EntityState.Added)
{
throw new InvalidOperationException(Strings.ObjectStateEntry_CannotModifyKeyProperty(memberName));
}
所以问题可能是 MyEntity 以某种方式被置于替代状态(例如 Modified
)在 SaveChanges()
之前(或在 SaveChanges()
但在标识值检索之前)?
怎么会这样? 正如我提到的,我没有在任何地方手动设置状态。
已解决。
我的堆栈跟踪底部的 MyNamespace.MyApp.MyContext.OnMyEvent()
行在预定时间执行。
事实证明,可以同时安排两次或多次执行。在某些情况下(即使在我的测试中它是不一致的并且通常仍然有效),它会导致崩溃,大概是因为当多个数据库插入在近乎瞬时接近的情况下完成时,SQLite 的 EF 生成的 ID 检索可能会被绊倒。
无论如何,解决方案是简单地将 MyNamespace.MyApp.MyContext.OnMyEvent()
中的代码包装在 lock { ... }
语句中以防止并发执行。
我管理一个公开可用的 WPF 应用程序,它使用 Entity Framework 6.1(模型优先的 ObjectContext)和一个 SQLite 数据库。
我的应用程序的第一个崩溃日志(根据 HockeyApp)如下(指向我的应用程序代码的指针标记为 ****):
System.InvalidOperationException: The property 'Id' is part of the object's key information and cannot be modified.
at System.Data.Entity.Core.Objects.EntityEntry.VerifyEntityValueIsEditable(StateManagerTypeMetadata typeMetadata, Int32 ordinal, String memberName)
at System.Data.Entity.Core.Objects.EntityEntry.GetAndValidateChangeMemberInfo(String entityMemberName, Object complexObject, String complexObjectMemberName, StateManagerTypeMetadata& typeMetadata, String& changingMemberName, Object& changingObject)
at System.Data.Entity.Core.Objects.EntityEntry.EntityMemberChanging(String entityMemberName, Object complexObject, String complexObjectMemberName)
at System.Data.Entity.Core.Objects.EntityEntry.EntityMemberChanging(String entityMemberName)
at System.Data.Entity.Core.Objects.ObjectStateEntry.System.Data.Entity.Core.Objects.DataClasses.IEntityChangeTracker.EntityMemberChanging(String entityMemberName)
at System.Data.Entity.Core.Objects.DataClasses.EntityObject.ReportPropertyChanging(String property)
****
at MyNamespace.MyApp.MyEntity.set_Id(Int64 value) in C:\MyPath\MyApp\EfGeneratedEntities.cs
****
at lambda_method(Closure , Object , Object )
at System.Data.Entity.Core.Objects.DelegateFactory.SetValue(EdmProperty property, Object target, Object value)
at System.Data.Entity.Core.Objects.StateManagerMemberMetadata.SetValue(Object userObject, Object value)
at System.Data.Entity.Core.Objects.Internal.LightweightEntityWrapper`1.SetCurrentValue(EntityEntry entry, StateManagerMemberMetadata member, Int32 ordinal, Object target, Object value)
at System.Data.Entity.Core.Objects.EntityEntry.SetCurrentEntityValue(StateManagerTypeMetadata metadata, Int32 ordinal, Object userObject, Object newValue)
at System.Data.Entity.Core.Objects.ObjectStateEntryDbUpdatableDataRecord.SetRecordValue(Int32 ordinal, Object value)
at System.Data.Entity.Core.Mapping.Update.Internal.PropagatorResult.SetServerGenValue(Object value)
at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.BackPropagateServerGen(List`1 generatedValues)
at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.<Update>b__2(UpdateTranslator ut)
at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update[T](T noChangesResult, Func`2 updateFunction)
at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update()
at System.Data.Entity.Core.Objects.ObjectContext.<SaveChangesToStore>b__d()
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.Core.Objects.ObjectContext.<>c__DisplayClassb.<SaveChangesInternal>b__8()
at System.Data.Entity.Infrastructure.DefaultExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
at System.Data.Entity.Core.Objects.ObjectContext.SaveChanges()
****
at MyNamespace.MyApp.MyContext.OnMyEvent() in C:\MyPath\MyApp\MyContext.cs
****
我无法重现此崩溃,因此无法尝试修复它。我很乐意对其进行排序。
我了解崩溃信息。但是我的应用程序没有做任何应该导致这种情况的事情。 MyEntity.Id
属性 是实体键(StoreGeneratedPattern
= Identity
),我从不手动设置 Id
值。我从不手动将实体标记为已修改。我的 SQLite 数据库没有任何触发器或任何其他可能会干扰 EF 对数据库生成的标识值的解释的东西。我真的看不出这种崩溃是怎么发生的,我自己也做不到 - 新实体的插入对我来说工作得很好。
我当然不指望任何人阅读这篇文章 post 并提出解决方案,但我希望有人能引导我朝着正确的方向前进。
哪些不明显的原因可能导致此崩溃发生?
我确定我会被要求提供源代码,但实际上并不多。在罪魁祸首代码中,我只是创建了一个 MyEntity
对象,设置了一些 属性 值,将其添加到上下文中,然后调用 SaveChanges()
。而已。正如我所说,它在我的测试和 99% 的客户使用中都运行良好。但是我每天都会收到好几份这样的崩溃报告,我很想结束它们。
----更新----
我认为值得指出的是,堆栈跟踪中的 MyEntity.set_Id(Int64 value)
显然是 Entity Framework 的 key/identity 值检索的一部分(参见 PropagatorResult.SetServerGenValue(Object value)
周围的行, ETC。)。这不是 my 设置 Id
的代码,而是 EF.
查看 Entity Framework source code,尝试为不处于 Added
状态的实体设置实体键值时抛出此异常:
// Key fields are only editable if the entry is the `Added` state.
if (member.IsPartOfKey
&& State != EntityState.Added)
{
throw new InvalidOperationException(Strings.ObjectStateEntry_CannotModifyKeyProperty(memberName));
}
所以问题可能是 MyEntity 以某种方式被置于替代状态(例如 Modified
)在 SaveChanges()
之前(或在 SaveChanges()
但在标识值检索之前)?
怎么会这样? 正如我提到的,我没有在任何地方手动设置状态。
已解决。
我的堆栈跟踪底部的 MyNamespace.MyApp.MyContext.OnMyEvent()
行在预定时间执行。
事实证明,可以同时安排两次或多次执行。在某些情况下(即使在我的测试中它是不一致的并且通常仍然有效),它会导致崩溃,大概是因为当多个数据库插入在近乎瞬时接近的情况下完成时,SQLite 的 EF 生成的 ID 检索可能会被绊倒。
无论如何,解决方案是简单地将 MyNamespace.MyApp.MyContext.OnMyEvent()
中的代码包装在 lock { ... }
语句中以防止并发执行。