实体框架5线程敏捷性
作者:互联网
抛出了EntityFramework代码深处的NullReferenceException(EF错误?),但是我的问题是关于Entity Framework(v5)和WebAPI异步控制器操作的.
在这里很难重新创建一个repro,但是从本质上讲,该代码执行以下操作:
public class AController : ApiController
{
private IUow _uow; //among other things, a DbContext
// DI ctor
public AController(IUow uow)
{
_uow = uow;
}
[HttpPost]
public async Task<HttpResponseMessage> Post(Model model)
{
Entity e = _uow.Entity.GetById(model.id);
await IO_Ops_Async(model);
new ModelAdapter().UpdateEntity(entity, model);
_uow.Commit(); <- EXCEPTION THROWN DURING THIS CALL - see below
... // do something with the return result
}
}
在Commit()内部,就在DbContext.SaveChanges()之前,我们遍历所有DbChangeTracker.Entries()来设置一些公共属性.但是,在System.Data.Entity.Infrastructure.DbChangeTracker.Entries()深入内部的NullReferenceException循环之前,Entries()会出错.
下面是调用堆栈.所有这些都是Framework代码,感觉像是个bug,但是我的问题确实是,是否允许上面的async / await在DbContext之间调用.我们从来没有使用多线程-仅使用async / await是因为可以使用async / await工具执行一些IO操作(几个Httpclient下载了一些异步磁盘I / O).
System.NullReferenceException: Object reference not set to an instance of an object.\r\n
at System.Data.Objects.DataClasses.RelatedEnd.IncludeEntity(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)\r\n
at System.Data.Objects.DataClasses.EntityReference`1.Include(Boolean addRelationshipAsUnchanged, Boolean doAttach)\r\n
at System.Data.Objects.DataClasses.RelatedEnd.WalkObjectGraphToIncludeAllRelatedEntities(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)\r\n
at System.Data.Objects.DataClasses.RelatedEnd.IncludeEntity(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)\r\n
at System.Data.Objects.DataClasses.EntityCollection`1.Include(Boolean addRelationshipAsUnchanged, Boolean doAttach)\r\n
at System.Data.Objects.DataClasses.RelatedEnd.WalkObjectGraphToIncludeAllRelatedEntities(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)\r\n
at System.Data.Objects.DataClasses.RelatedEnd.IncludeEntity(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)\r\n
at System.Data.Objects.DataClasses.EntityReference`1.Include(Boolean addRelationshipAsUnchanged, Boolean doAttach)\r\n
at System.Data.Objects.DataClasses.RelatedEnd.WalkObjectGraphToIncludeAllRelatedEntities(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)\r\n
at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges)\r\n
at System.Data.Objects.ObjectStateManager.PerformAdd(IEntityWrapper wrappedOwner, RelatedEnd relatedEnd, IEntityWrapper entityToAdd, Boolean isForeignKeyChange)\r\n
at System.Data.Objects.ObjectStateManager.PerformAdd(IList`1 entries)\r\n
at System.Data.Objects.ObjectStateManager.DetectChanges()\r\n
at System.Data.Entity.Internal.InternalContext.GetStateEntries(Func`2 predicate)\r\n
at System.Data.Entity.Infrastructure.DbChangeTracker.Entries()\r\n
解决方法:
由于IO完成,等待后会有一个隐式线程切换. AFAIK,EF5可能无法处理此问题,因为它使用线程本地存储.
在这种情况下,OTOH,EF6.x(尤其是最新版本)应该可以正常工作.
相关:How to use non-thread-safe async/await APIs and patterns with ASP.NET Web API?
更新以解决评论:
Because the async/await infrastructure supposedly take care and flows
the ExecutionContext (thread-local storage among other “contexts”). I
am asking so that I can make educated changes and preserve the
async/await implementation taking care of whatever specific thing is
breaking EF.
EF5源代码不是开源的(与EF6不同),所以我不能100%确定,但是我怀疑EF5明确使用TLS(即ThreadStatic或ThreadLocal< T>). ExecutionContext不可能自动流动所有TLS属性.这将是对现有代码的巨大突破性变化和安全威胁(更不用说在技术上甚至不可能实现这一点).
ExecutionContext捕获并流动a very specific subset of thread properties.该子集未记录,但您可以了解更多有关here的信息.
跨多个线程传递其静态属性是特定类实现的负责任,为此提供了CallContext.LogicalSetData / CallContext.LogicalGetData.我相信这就是EF6的内幕.
标签:async-await,nullreferenceexception,entity-framework-5,c,entity-framework 来源: https://codeday.me/bug/20191121/2055100.html