Entity Framework 5 Thread Agility

NullReferenceException error inside EntityFramework code (EF error?), But my question is about the actions of the Entity Framework (v5) asynchronous controller and WebAPI.

Reproduction is difficult to recreate here, but essentially the code does the following:

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 } } 

Inside Commit() , before DbContext.SaveChanges() , we will skip all DbChangeTracker.Entries() to set some common properties. But it's Entries() that errors before a single loop with a NullReferenceException deep inside System.Data.Entity.Infrastructure.DbChangeTracker.Entries()

Below is the call stack. This is all Framework code, and it feels like a bug, but actually my question is whether using the above async / awake between DbContext calls is allowed. In no case do we use multi-thread - async / await, because there are several I / O operations that we can perform using the async / await tool (a couple of Httpclient downloads + some asynchronous disk 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 
+2
c # nullreferenceexception entity-framework-5 entity-framework async-await
Apr 09 '14 at 20:21
source share
1 answer

There is an implicit thread switch after await caused by I / O completion. AFAIK, EF5, may not be able to handle this because it uses local thread storage.

OTOH, EF6.x (especially the latest release) should work fine in this case.

Related: How to use non-stream async / await APIs and ASP.NET APIs?

Updated to respond to comment:

Since the asynchronous / waiting service infrastructure supposedly takes care of the threads and ExecutionContext (local storage of threads among other "contexts"). I ask that I can make educated changes and keep async / waiting for implementation, taking care of any particular thing EF violation.

EF5 source code is not open source (unlike EF6), so I cannot be 100% sure, but I suspect that EF5 explicitly uses TLS (i.e. ThreadStatic or ThreadLocal<T> ). It is not possible that all TLS properties are automatically passed using ExecutionContext . This would be a huge violation of the changes and a security risk for the existing code (not to mention this, it may even be technically impossible to implement this).

ExecutionContext captures and passes a very specific subset of stream properties . This subset is undocumented, but you can learn more about it here .

Responsibility for the concrete implementation of the class for transferring its static properties to multiple threads, there CallContext.LogicalSetData / CallContext.LogicalGetData for this. I believe this is what the EF6 does under the hood.

+3
Apr 09 '14 at 20:34
source share



All Articles