实体框架5线程敏捷性

3

在Entity Framework(v5)和WebAPI异步控制器操作中,会抛出一个深层次的NullReferenceException异常(EF bug?)。但我的问题是关于Entity Framework的。

这里很难重现,但代码本质上做了以下几件事:

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调用之间是否允许使用以上async/await。我们没有多线程 - async/await只是因为有一些IO操作可以使用async/await功能执行(一些HttpClient下载+一些异步磁盘IO)。
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
1个回答

3
await 后会隐式进行线程切换,由IO完成引起。据我所知,EF5 可能无法处理此操作,因为它使用线程本地存储。
另一方面,EF6.x(尤其是最新版本)应该可以在这种情况下正常工作。
相关问题:如何在ASP.NET Web API中使用非线程安全的async / await APIs和模式? 更新以回复评论:
因为 async/await 基础设施被认为会处理并流动 ExecutionContext(线程本地存储等“上下文”)。 我之所以问这个问题,是为了可以做出明智的更改并保留 async/await 实现,同时解决 EF 破坏的任何特定问题。
EF5 源代码不是开源的(与 EF6 不同),因此我不能百分之百确定,但我怀疑 EF5 明确使用 TLS(即 ThreadStatic 或 ThreadLocal)。不可能 自动 流动所有 TLS 属性。 这将是一个巨大的破坏性变化和安全风险(更不用说它甚至可能无法实现)。
ExecutionContext 捕获并流动 非常特定的线程属性。 这个子集是未经记录的,但您可以在这里了解更多信息。
将静态属性在多个线程之间流动是特定类实现的职责,有 CallContext.LogicalSetData / CallContext.LogicalGetData 可以实现。 我相信这就是EF6在幕后所做的。

1
据我所知,EF5可能无法处理这个问题。我看到过类似的评论(其中一些是你的),但我想知道你是否有详细信息。因为异步/等待基础设施据说会处理并流动ExecutionContext(线程本地存储和其他“上下文”之间)。我在询问这个问题,以便我可以做出明智的更改,并保留异步/等待实现,同时照顾任何特定的东西正在破坏EF。 - G. Stoynev
谢谢您添加信息。之前我能够追踪到早期版本 System.Data.Entity.dll 的参考来源(不确定是 4 还是 5),但我想您指的是 EntityFramework.dll,对吗? - G. Stoynev
@G.Stoynev,我认为EF5+仍然使用这两个DLL,请查看此链接 - noseratio - open to work

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接