在NullReferenceException代码中抛出一个EntityFramework代码(EF?),但我的问题是实体框架(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()来设置一些公共属性。但是,是Entries()在单个循环之前发生错误,NullReferenceException在System.Data.Entity.Infrastructure.DbChangeTracker.Entries()中很深。
下面是调用堆栈。这都是框架代码,感觉就像一个bug,但我的问题是,上面的异步/等待是否允许在DbContext调用之间使用。在任何时候,我们只使用多线程异步/等待,因为我们可以使用异步/等待工具执行一些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发布于 2014-04-09 20:34:51
在await之后有一个由IO完成引起的隐式线程切换。AFAIK,EF5可能无法处理这一点,因为它使用线程本地存储。
OTOH、EF6.x (特别是最新的版本)在这种情况下应该可以正常工作。
相关:如何在ASP.NET Web中使用非线程安全的异步/等待API和模式?
更新了以回应这一评论:
因为异步/等待基础设施应该注意并流ExecutionContext (线程-本地存储和其他“上下文”)。我这样做是为了使我能够进行有教养的更改,并保留异步/等待实现,同时处理任何破坏EF的特定事情。
EF5源代码不是开源的(与EF6不同),所以我不能100%确定,但我怀疑EF5显式地使用了TLS (即ThreadStatic或ThreadLocal<T>)。automatically ExecutionContext**.**不可能将的所有 TLS属性都转换为,这将是对现有代码的巨大破坏和安全威胁(更不用说它在技术上甚至不可能实现这一点)。
ExecutionContext捕获并流线程属性的一个非常特定的子集。这个子集是没有文档的,但是您可以了解更多关于它的这里。
它是特定类实现的一个负责任的,它的静态属性在多个线程之间流动,它有CallContext.LogicalSetData/CallContext.LogicalGetData。我相信这就是EF6在引擎盖下所做的。
https://stackoverflow.com/questions/22973162
复制相似问题