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

Query fails with JSON columns and Include collection #28808

Closed
ajcvickers opened this issue Aug 21, 2022 · 4 comments · Fixed by #28832 or #28974
Closed

Query fails with JSON columns and Include collection #28808

ajcvickers opened this issue Aug 21, 2022 · 4 comments · Fixed by #28832 or #28974
Assignees
Labels
area-json area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. priority-bug Issues which requires API breaks and have bigger impact hence should be fixed earlier in the release type-bug
Milestone

Comments

@ajcvickers
Copy link
Member

context.Authors.Include(author => author.Posts).ToList();

Author has a JSON column. Full code below.

Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.UnaryExpression' to type 'System.Linq.Expressions.ParameterExpression'.
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ProcessShaper(Expression shaperExpression, RelationalCommandCache& relationalCommandCache, 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 System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   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__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.IncludableQueryable`2.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Program.Main() in C:\local\code\AllTogetherNow\Daily\Daily.cs:line 149
#nullable enable

public static class Your
{
    public static string ConnectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;Database=AllTogetherNow";
}

public class Blog
{
    public Blog(string name)
    {
        Name = name;
    }

    public int Id { get; private set; }
    public string Name { get; set; }
    public List<Post> Posts { get; } = new();
}

public class Post
{
    public Post(string title, string content, DateTime publishedOn)
    {
        Title = title;
        Content = content;
        PublishedOn = publishedOn;
    }

    public int Id { get; private set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime PublishedOn { get; set; }
    public Blog Blog { get; set; } = null!;
    public List<Tag> Tags { get; } = new();
    public Author? Author { get; set; }
}

public class FeaturedPost : Post
{
    public FeaturedPost(string title, string content, DateTime publishedOn, string promoText)
        : base(title, content, publishedOn)
    {
        PromoText = promoText;
    }

    public string PromoText { get; set; }
}

public class Tag
{
    public Tag(string text)
    {
        Text = text;
    }

    public int Id { get; private set; }
    public string Text { get; set; }
    public List<Post> Posts { get; } = new();
}

public class Author
{
    public Author(string name)
    {
        Name = name;
    }

    public int Id { get; private set; }
    public string Name { get; set; }
    public ContactDetails Contact { get; set; } = null!;
    public List<Post> Posts { get; } = new();
}

public class ContactDetails
{
    public Address Address { get; init; } = null!;
    public string? Phone { get; init; }
}

public class Address
{
    public Address(string street, string city, string postcode, string country)
    {
        Street = street;
        City = city;
        Postcode = postcode;
        Country = country;
    }

    public string Street { get; init; }
    public string City { get; init; }
    public string Postcode { get; init; }
    public string Country { get; init; }
}

public class SomeDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlServer(Your.ConnectionString)
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();

    public DbSet<Blog> Blogs => Set<Blog>();
    public DbSet<Tag> Tags => Set<Tag>();
    public DbSet<Post> Posts => Set<Post>();
    public DbSet<Author> Authors => Set<Author>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<FeaturedPost>();
        modelBuilder.Entity<Author>().OwnsOne(
            author => author.Contact, ownedNavigationBuilder =>
                {
                    ownedNavigationBuilder.ToJson();
                    ownedNavigationBuilder.OwnsOne(contactDetails => contactDetails.Address);
                });
    }
}

public class Program
{
    public static void Main()
    {
        using (var context = new SomeDbContext())
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();
        
            var authorsInChigleyWithPosts = context.Authors
                .Include(author => author.Posts)
                .ToList();
        }
    }
}
@ajcvickers
Copy link
Member Author

Note for triage: we should consider fixing this for RC1.

@maumar
Copy link
Contributor

maumar commented Aug 21, 2022

In json shaper, we always assume that the outer entity will be a ParameterExpression, but in case of include it can be resultContext access with conversion. Fix is to relax the CreateJsonShapers method to accept any expression rather than parameter.

Edit: issue is bit more complicated than just type change. We only populate the result context after we have visited the entity shaper, but in case of json we are accessing that entity while we are constructing the related json entity shapers. @smitpatel

@maumar maumar changed the title Query fails with JSON columns and Include Query fails with JSON columns and Include collection Aug 21, 2022
@smitpatel
Copy link
Contributor

smitpatel commented Aug 21, 2022

We only populate the result context after we have visited the entity shaper,

that is true.

but in case of json we are accessing that entity while we are constructing the related json entity shapers.

While this is not. You don't access any actual value when constructing the shaper, you only use the associated parameter/variable. Flow-wise you should only invoke the code which needs the actual instance in generated code only after the entity instance has been created and stored in the result context.

Edit: Happy to fix this issue if you want while you are focusing on JSON updates.

@smitpatel smitpatel self-assigned this Aug 22, 2022
smitpatel added a commit that referenced this issue Aug 22, 2022
Resolves #28808

The entity instance is in resultContext.Values. We were trying to use it before creating resultContext.Values.
Fix is to materialize the includes for JSON after resultContext.Values has been created.

The collection include introduces the resultContext.
smitpatel pushed a commit that referenced this issue Aug 22, 2022
Resolves #28808

The entity instance is in resultContext.Values. We were trying to use it before creating resultContext.Values.
Fix is to materialize the includes for JSON after resultContext.Values has been created.

The collection include introduces the resultContext.
@smitpatel smitpatel added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Aug 22, 2022
@smitpatel smitpatel added this to the 7.0.0-rc1 milestone Aug 22, 2022
@ajcvickers
Copy link
Member Author

This now fails with:

info: 8/30/2022 20:00:10.269 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [p].[Id], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Discriminator], [p].[PublishedOn], [p].[Title], [p].[PromoText], JSON_QUERY([p].[Metadata],'$')
      FROM [Posts] AS [p]
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.ValueTuple`3[System.Int32,System.Collections.Generic.List`1[System.ValueTuple`2[Microsoft.EntityFrameworkCore.Metadata.IProperty,System.Int32]],System.String[]]' to type 'System.Int32'.
   at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.<ApplyProjection>g__CopyProjectionToOuter|61_8(SelectExpression innerSelectExpression, Expression innerShaperExpression, <>c__DisplayClass61_0& )
   at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.ApplyProjection(Expression shaperExpression, ResultCardinality resultCardinality, QuerySplittingBehavior querySplittingBehavior)
   at Microsoft.EntityFrameworkCore.Query.Internal.SelectExpressionProjectionApplyingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryTranslationPostprocessor.Process(Expression query)
   at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerQueryTranslationPostprocessor.Process(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__DisplayClass12_0`1.<ExecuteAsync>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   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 Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.IncludableQueryable`2.GetAsyncEnumerator(CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at NewInEfCore7.JsonColumnsSample.JsonColumnsTest[TContext]() in C:\github\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore7\JsonColumnsSample.cs:line 85
   at NewInEfCore7.JsonColumnsSample.JsonColumnsTest[TContext]() in C:\github\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore7\JsonColumnsSample.cs:line 138
   at Program.Main() in C:\github\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore7\Program.cs:line 23
   at Program.<Main>()

@ajcvickers ajcvickers removed this from the 7.0.0-rc1 milestone Aug 30, 2022
@ajcvickers ajcvickers removed the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Aug 30, 2022
@ajcvickers ajcvickers added this to the 7.0.0 milestone Aug 31, 2022
@ajcvickers ajcvickers added the priority-bug Issues which requires API breaks and have bigger impact hence should be fixed earlier in the release label Aug 31, 2022
@smitpatel smitpatel added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Sep 3, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0, 7.0.0-rc2 Sep 9, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0-rc2, 7.0.0 Nov 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-json area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. priority-bug Issues which requires API breaks and have bigger impact hence should be fixed earlier in the release type-bug
Projects
None yet
3 participants