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

Split query with AsNoTrackingWithIdentityResolution() throws ArgumentOutOfRangeException #34728

Closed
bkoelman opened this issue Sep 20, 2024 · 12 comments · Fixed by #34742
Closed
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported regression type-bug
Milestone

Comments

@bkoelman
Copy link

bkoelman commented Sep 20, 2024

File a bug

When running our test suite against EF Core 9 RC1, I see various cases where an ArgumentOutOfRangeException is thrown. These tests succeed when run against the latest versions of EF Core 8 and 6, as well as 9.0.0-preview.3.24172.4. I've verified the crash happens both with Npgsql and Sqlite, which is why I believe the issue is in EF Core and not provider-related. It appears the problem was introduced in v9.0.0-preview.7.24405.3.

Steps to reproduce

  1. Create an empty console app that targets .NET 9. Add package references to Microsoft.EntityFrameworkCore.Sqlite (9.0.0-rc.1.24451.1) and Npgsql.EntityFrameworkCore.PostgreSQL (9.0.0-rc.1).
  2. Paste the contents below in Program.cs and run.
  3. Observe the crash (see stack trace below).
  4. To verify with Postgres, start a docker container:
    docker run --pull always --rm --detach -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:latest
    
    and use the commented-out lines.

Include your code

Contents of Program.cs:

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddDbContext<AppDbContext>(options =>
{
    //options.UseNpgsql("Host=localhost;Database=Demo;User ID=postgres;Password=postgres;Include Error Detail=true",
    //    builder => builder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));

    options.UseSqlite("Data Source=SampleDb.db;Pooling=False",
        builder => builder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
});

var serviceProvider = services.BuildServiceProvider();
await using var scope = serviceProvider.CreateAsyncScope();
var appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();

await appDbContext.Database.EnsureDeletedAsync();
await appDbContext.Database.EnsureCreatedAsync();

var query = appDbContext.Blogs
    .AsNoTrackingWithIdentityResolution()
    .Select(
        blog => new Blog
        {
            Id = blog.Id,
            Posts = blog.Posts
                .Select(
                    blogPost => new BlogPost
                    {
                        Id = blogPost.Id,
                        Author = blogPost.Author
                    })
                .ToHashSet()
        });

_ = await query.ToArrayAsync();

public sealed class AppDbContext(DbContextOptions options) : DbContext(options)
{
    public DbSet<Blog> Blogs => Set<Blog>();
}

public sealed class Blog
{
    public long Id { get; set; }
    public ISet<BlogPost> Posts { get; set; } = new HashSet<BlogPost>();
}

public sealed class BlogPost
{
    public long Id { get; set; }
    public WebAccount? Author { get; set; }
}

public sealed class WebAccount
{
    public long Id { get; set; }
}

Include stack traces

Unhandled exception. System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
   at System.Collections.Generic.List`1.get_Item(Int32 index)
   at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.GetProjection(ProjectionBindingExpression projectionBindingExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.JsonCorrectOrderOfEntitiesForChangeTrackerValidator.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberAssignment(MemberAssignment node)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
   at System.Linq.Expressions.ExpressionVisitor.Visit[T](ReadOnlyCollection`1 nodes, Func`2 elementVisitor)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.JsonCorrectOrderOfEntitiesForChangeTrackerValidator.VisitExtension(Expression extensionExpression)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberAssignment(MemberAssignment node)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
   at System.Linq.Expressions.ExpressionVisitor.Visit[T](ReadOnlyCollection`1 nodes, Func`2 elementVisitor)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.JsonCorrectOrderOfEntitiesForChangeTrackerValidator.Validate(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ProcessShaper(Expression shaperExpression, Expression& relationalCommandResolver, IReadOnlyList`1& readerColumns, LambdaExpression& relatedDataLoaders, Int32& collectionId)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.VisitShapedQuery(ShapedQueryExpression shapedQueryExpression)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutorExpression[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass11_0`1.<ExecuteCore>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteCore[TResult](Expression query, Boolean async, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToArrayAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Program.<Main>$(String[] args) in D:\Bart\Source\Projects\EFCore9BugRepro\EFCore9BugRepro\Program.cs:line 37
   at Program.<Main>$(String[] args) in D:\Bart\Source\Projects\EFCore9BugRepro\EFCore9BugRepro\Program.cs:line 37
   at Program.<Main>(String[] args)
@roji
Copy link
Member

roji commented Sep 21, 2024

Confirmed regression in 9.0, specifically for AsNoTrackingWithIdentityResolution().

@maumar interested in taking a look? I think we'd consider this for servicing.

@maumar
Copy link
Contributor

maumar commented Sep 23, 2024

Error is caused by a bug in JsonCorrectOrderOfEntitiesForChangeTrackerValidator, specifically it uses the initial SelectExpression to analyze structure of various shaper expressions in the query. Problem is that RelationalSplitCollectionShaperExpression has its own SelectExpression that desribed the collection - we should use that select expression rather than the parent

@roji roji removed their assignment Sep 23, 2024
@roji
Copy link
Member

roji commented Sep 23, 2024

Thanks for investigating @maumar! Note that this is a regression (bug didn't happen in 8.0 AFAICT), any idea of the change that triggered this?

In any case, we should consider prioritizing this as a regression, even though it's somewhat edge-casey, requiring both AsNoTrackingWithIdentityResolution() and split query, as well as this specific query shape.

@roji roji changed the title Breaking: ArgumentOutOfRangeException in v9-RC1 Split query with AsNoTrackingWithIdentityResolution() throws ArgumentOutOfRangeException Sep 23, 2024
@maumar
Copy link
Contributor

maumar commented Sep 23, 2024

@roji #33073 is the culprit. To our credit, we identified it as a risky fix and we didn't port it to 8, even though the original issue was a regression as well.

@roji
Copy link
Member

roji commented Sep 23, 2024

Sounds good, thanks for looking into it @maumar!

maumar added a commit that referenced this issue Sep 23, 2024
… throws ArgumentOutOfRangeException

This is a regression introduced in 9.0 when trying to address a different regression (#33073)

Error is caused by a bug in JsonCorrectOrderOfEntitiesForChangeTrackerValidator, specifically it uses the initial SelectExpression to analyze structure of various shaper expressions in the query. Problem is that RelationalSplitCollectionShaperExpression has its own SelectExpression that described the collection - we should use that select expression rather than the parent.
Fix is to update SelectExpression used to process the expression when are processing RelationalSplitCollectionShaperExpression

Fixes #34728
maumar added a commit that referenced this issue Sep 23, 2024
… throws ArgumentOutOfRangeException

Port of #34742

Fixes #34728

Description

Error is caused by a bug in JsonCorrectOrderOfEntitiesForChangeTrackerValidator, specifically it uses the initial SelectExpression to analyze structure of various shaper expressions in the query. Problem is that RelationalSplitCollectionShaperExpression has its own SelectExpression that described the collection - we should use that select expression rather than the parent.

Customer impact

Some queries using split query and AsNoTrackingWithIdentityResolution fail during compilation with a cryptic error (index out of range) there is no good workaround outside of not using split query and/or AsNoTrackingWithIdentityResolution. The scenario is quite niche, requiring two query customizations (split query and AsNoTrackingWithIdentityResolution) as well as a specific shape.

How found
Customer reported on 9 RC1.

Regression
Yes, introduced in 9 while attempting to fix another regression from EF8 (#33073)

Testing
Multiple tests added.

Risk
Low. Change is straightforward - updating the state used in one of our visitors, we use that strategy in numerous places. The visitor itself is used for validation - it doesn't manipulate the shape of the query.
@maumar maumar added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Sep 24, 2024
@maumar maumar closed this as completed in 00b08d7 Sep 24, 2024
maumar added a commit that referenced this issue Sep 24, 2024
… throws ArgumentOutOfRangeException (#34742)

This is a regression introduced in 9.0 when trying to address a different regression (#33073)

Error is caused by a bug in JsonCorrectOrderOfEntitiesForChangeTrackerValidator, specifically it uses the initial SelectExpression to analyze structure of various shaper expressions in the query. Problem is that RelationalSplitCollectionShaperExpression has its own SelectExpression that described the collection - we should use that select expression rather than the parent.
Fix is to update SelectExpression used to process the expression when are processing RelationalSplitCollectionShaperExpression

Fixes #34728
@bkoelman
Copy link
Author

Thanks, @roji and @maumar, for the quick processing of this.

Once this is in the daily builds, I'll verify it against our codebase.

maumar added a commit that referenced this issue Sep 25, 2024
… throws ArgumentOutOfRangeException (#34742) (#34743)

This is a regression introduced in 9.0 when trying to address a different regression (#33073)

Error is caused by a bug in JsonCorrectOrderOfEntitiesForChangeTrackerValidator, specifically it uses the initial SelectExpression to analyze structure of various shaper expressions in the query. Problem is that RelationalSplitCollectionShaperExpression has its own SelectExpression that described the collection - we should use that select expression rather than the parent.
Fix is to update SelectExpression used to process the expression when are processing RelationalSplitCollectionShaperExpression

Fixes #34728
@bkoelman
Copy link
Author

I've tried using the following packages from the dotnet9 feed, but I still get the same error.

It looks like #34743 (merged 20 hours ago) hasn't landed there yet. Or am I doing something wrong?

@roji
Copy link
Member

roji commented Sep 26, 2024

@bkoelman try again in a couple days.

@bkoelman
Copy link
Author

It looks like the fix made it into 9.0.0-rtm.24508.5 from the dotnet9 feed, but 9.0.0-rc.2.24474.1 on nuget.org doesn't contain it.

@roji
Copy link
Member

roji commented Oct 10, 2024

@bkoelman yep, that makes sense - we branched for rc.2 a while ago.

@bkoelman
Copy link
Author

Can you confirm this fix will ship in EF9 RTM? With the fix, our ~9000 tests are green now.

@roji
Copy link
Member

roji commented Oct 11, 2024

Yes - that's what it means for the fix to be in 9.0.0-rtm.24508.5.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported regression type-bug
Projects
None yet
3 participants