Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EF63 (and EF 6.4.0-preview1) - NullReferenceException on materializing objects with AsNoTracking() #1408

Closed
jonnybee opened this issue Oct 22, 2019 · 12 comments

Comments

@jonnybee
Copy link

jonnybee commented Oct 22, 2019

Our app breaks on data access after upgrading from EF 6.2 to EF 6.3.

Sample code:

var expertId = 12345678;

var expert = await Expert.AsNoTracking()
	.Include(p => p.ExpertEducation)
.Include(p => p.ExpertPosition)
.Include(p => p.ExpertReference)
.Include(p => p.ExpertResponsibility)
.Include(p => p.ExpertSkill)
.Include(p => p.ExpertLanguage)
.Include(p => p.ExpertProject)
.Include(p => p.ExpertCulture)
.Include(p => p.ExpertUrl)
.Include(p => p.ExpertObject)
.Include(p => p.ExpertTraining)
.Include(p => p.ExpertSpecialisation)
.Where(p => p.ExpertId == expertId)
.FirstOrDefaultAsync();

When using AsNoTracing() to load entitites without ChangeTracking a NullReference exception is thrown. With ChangeTracking load data works fine.

The NullReference exception is thrown in method RelatedEnd.GetOtherEndOfRelationship (IEntityWrapper wrappedEntity) and the full stacktrace is:

at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.GetOtherEndOfRelationship(IEntityWrapper wrappedEntity)   at System.Data.Entity.Core.Objects.ObjectStateManager.AddEntityToCollectionOrReference(MergeOption mergeOption, IEntityWrapper wrappedSource, AssociationEndMember sourceMember, IEntityWrapper wrappedTarget, AssociationEndMember targetMember, Boolean setIsLoaded, Boolean relationshipAlreadyExists, Boolean inKeyEntryPromotion)   at System.Data.Entity.Core.Objects.ObjectStateManager.UpdateRelationships(ObjectContext context, MergeOption mergeOption, AssociationSet associationSet, AssociationEndMember sourceMember, IEntityWrapper wrappedSource, AssociationEndMember targetMember, IList targets, Boolean setIsLoaded)   at System.Data.Entity.Core.Common.Internal.Materialization.Shaper.FullSpanAction[TTargetEntity](IEntityWrapper wrappedSource, IList1 spannedEntities, AssociationEndMember targetMember)   at System.Data.Entity.Core.Common.Internal.Materialization.Shaper.<>c__DisplayClass12_01.b__0(Shaper state, List1 spannedEntities)   at System.Data.Entity.Core.Common.Internal.Materialization.Coordinator1.ResetCollection(Shaper shaper)   at System.Data.Entity.Core.Common.Internal.Materialization.Coordinator1.ResetCollection(Shaper shaper)   at System.Data.Entity.Core.Common.Internal.Materialization.Shaper1.RowNestedResultEnumerator.d__11.MoveNext()--- End of stack trace from previous location where exception was thrown ---   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)   at System.Data.Entity.Core.Common.Internal.Materialization.Shaper1.ObjectQueryNestedEnumerator.<TryReadToNextElementAsync>d__16.MoveNext()--- End of stack trace from previous location where exception was thrown ---   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)   at System.Data.Entity.Core.Common.Internal.Materialization.Shaper1.ObjectQueryNestedEnumerator.d__14.MoveNext()--- End of stack trace from previous location where exception was thrown ---   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)   at System.Data.Entity.Core.Common.Internal.Materialization.Shaper1.ObjectQueryNestedEnumerator.<MoveNextAsync>d__12.MoveNext()--- End of stack trace from previous location where exception was thrown ---   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)   at System.Data.Entity.Internal.LazyAsyncEnumerator1.d__9.MoveNext()--- End of stack trace from previous location where exception was thrown ---   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)   at System.Data.Entity.Infrastructure.IDbAsyncEnumerableExtensions.d__241.MoveNext()--- End of stack trace from previous location where exception was thrown ---   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)   at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()   at UserQuery.d__1.MoveNext()

Another observation is that the generated SQL now has an CROSS APPLY on the last table which is not generated with EF 6.2.

Same NullReferenceException is also thrown when using EF 6.4.0-preview1.

@ajcvickers
Copy link
Member

@jonnybee Please post a small, runnable project/solution or complete code listing that demonstrates the behavior you are seeing so that we can fully investigate.

@jonnybee
Copy link
Author

I have investigated a bit more.

Our app runs against both Oracle and SqlAzure database with the same schema definitions and only data access to Oracle fails after the upgrade. Are there any braking changes in the specification of EF Provider when moving from EF 6.2 to EF 6.3? We use the lastest official Oracle packages.

So the big question then is whether this is an issue in Entity Framework 6.3 or in Oracle.ManagedDataAccess.EntityFramework 19.3.0

@ErikEJ
Copy link
Contributor

ErikEJ commented Oct 25, 2019

Has Oracle.ManagedDataAccess.EntityFramework been updated to support 6.3?

@jonnybee
Copy link
Author

Are there braking changes in the provider model when going from EF 6.2 to 6.3?

The Oracle.ManagedDataAccess.EntityFramework was updated 5 months ago and list dependencies as

  • EntityFramework (>= 6.0.0 && < 7.0.0)
  • Oracle.ManagedDataAccess (>= 19.3.0 && < 20.0.0)

@ajcvickers
Copy link
Member

@jonnybee The 6.2 provider should work, assuming that it is being used with full .NET Framework (not .NET Standard or .NET Core) and NuGet packages.config (not package references). There were no intentional, runtime breaking changes, but it's possible a bug fix/community PR could have broken something. We will likely still need a repro project to investigate.

@ajcvickers
Copy link
Member

EF Team Triage: Closing this issue as the requested additional details have not been provided and we have been unable to reproduce it.

BTW this is a canned response and may have info or details that do not directly apply to this particular issue. While we'd like to spend the time to uniquely address every incoming issue, we get a lot traffic on the EF projects and that is not practical. To ensure we maximize the time we have to work on fixing bugs, implementing new features, etc. we use canned responses for common triage decisions.

@efeblesp
Copy link

I know this issues is closed but perhaps someone sees this. Was this resolved? We are investigating similar issue and it seems to show when we added a new column to an entity that is part of a similar "big" include as no tracking. The mappings seems fine, and it really looks like there are "too many" entities and fields in the include. Does this makes any sense? How was your issues resolved?

@jonnybee
Copy link
Author

jonnybee commented Feb 13, 2021

Something changed from v 6.2 to 6.3 and we have not been able to upgrade to later versions. The isssue has never been resolved and still happens in version 6.4.4.

It only happens when we have many Include and AsNotTracking(). Without AsNoTracking it runs fine.

I haven't had time to create a reproduction of the issue. BTW - We have only seen this with Oracle Database.

@jonnybee
Copy link
Author

It starts to fail in our code from EF 6.3-preview7-19363-02.

It may be a coinsidence but this is also when the change in JOIN syntax happens. Starting in this version the last "INNER JOIN" in previous versions is now a CROSS APPLY.

I can also confirm that it seems to happen when there is many columns. Seems to happen when there is 370+ columns.

@efeblesp
Copy link

We are on the same ballpark number of columns for the resulting oracle query. It’s so weird that just changing the order of the includes makes the entity query works.
Thanks for the tip, we are going to try to lower to 6.2 and see. I am just glad I am not alone and it’s not something we are clearly doing wrong.

@jonnybee
Copy link
Author

jonnybee commented Feb 15, 2021

I've been digging into this issue this weekend and it was caused by commit 2483303 "Query compilation performance" #430 and this change:

    private const int MaxNodeCountForTransformations = 100000;   // from 
    private const int MaxNodeCountForTransformations = 10000;     // to 

For my app I could workaround by adding this switch to the web.config/app.config

<configuration>  
    <system.diagnostics>  
         <switches>  
                 <add name="System.Data.Entity.Core.EntityClient.IgnoreOptimizationLimit" value="1"/>  
         </switches>  
    </system.diagnostics>  
 </configuration> 

@ajcvickers @cjpearson Is there a good reason for the change of MaxNodeCountForTransformations?

When you have large queries that hit this threshold in the PlanOptimizer (+370 columns and a number of tables) the ObjectStateManager.AddEntityToCollectionOrReference may get a NullEntityWrapper in wrappedTarget parameter and this blows up in a NullReferenceException in GetOtherEndOfRelationship:

    internal RelatedEnd GetOtherEndOfRelationship(IEntityWrapper wrappedEntity)
    {
        DebugCheck.NotNull(wrappedEntity);
        EnsureRelationshipNavigationAccessorsInitialized();
        return wrappedEntity.RelationshipManager.GetRelatedEnd(_navigation.Reverse, _relationshipFixer);
    }

where a NullEntityWrapper returns

    public RelationshipManager RelationshipManager
    {
        get
        {
            Debug.Fail("Cannot access RelationshipManager from null wrapper.");
            return null;
        }
    }

At the very least - EF should give a more precise exception to help developers to find the workaround or maybe revert the change of MaxNodeCountForTransformations. As it is now - at a "random" point the materializing fails on large queries with a NullReferenceException with no hint to the developer as to why this happens.

Could there also be other side effects of that change? If we remove AsNoTracking() there is no exception thrown but do we get the correct data in the result?

PS: So far this has only happened when running EF against Oracle in our app - against SqlServer seems to be working fine for us at least..

@efeblesp
Copy link

@ajcvickers Arthur, any chance you could look into this closed thread?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants