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

ArgumentException thrown when building queries involving owned entities mapped with .ToJson() #33046

Closed
bperc2 opened this issue Feb 9, 2024 · 3 comments · Fixed by #33110
Closed
Assignees
Labels
area-model-building area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Milestone

Comments

@bperc2
Copy link

bperc2 commented Feb 9, 2024

After updating my project from versions 7.0.15 to 8.0.1, I'm encountering an exception with queries against a model that used to work before the upgrade. The part of the model that appears to be the hang-up involves:

  • An entity that owns many of something mapped with .ToJson()
  • That owned entity owns many of another kind of entity
  • These relationships involve properties and fields of differing types (property is an interface, field is concrete)

Here is a runnable example to demonstrate

using Microsoft.EntityFrameworkCore;

try
{
    using TestContext db = new();
    db.Database.EnsureCreated();

    var allOneLevelReviews = db.OneLevelReviews.ToList();

    // Exception occurs here
    var allReviews = db.Reviews.ToList();
    Console.WriteLine("OK");

}
catch (Exception ex)
{
    var exceptionString = ex.ToString();
    Console.WriteLine(ex.ToString());
}

Console.ReadLine();


public class Review
{
    public int Id { get; set; }

    private List<ReviewRound> _rounds = [];
    public IReadOnlyList<ReviewRound> Rounds => _rounds.AsReadOnly();
}

public class ReviewRound
{
    public int RoundNumber { get; set; }

    private List<SubRound> _subRounds = [];
    public IReadOnlyList<SubRound> SubRounds => _subRounds.AsReadOnly();
}

public class SubRound
{
    public int SubRoundNumber { get; set; }

}

public class OneLevelReview
{
    public int Id { get; set; }

    private List<SubRound> _subRounds = [];
    public IReadOnlyList<SubRound> SubRounds => _subRounds.AsReadOnly();
}



public class TestContext : DbContext
{
    public DbSet<Review> Reviews { get; set; }
    public DbSet<OneLevelReview> OneLevelReviews { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        options
            .UseSqlServer(
                "...",
                providerOptions => providerOptions.EnableRetryOnFailure())
            .EnableDetailedErrors()
            .EnableSensitiveDataLogging();
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Review>().OwnsMany(x => x.Rounds, ownedBuilder =>
        {
            ownedBuilder.ToJson();

            ownedBuilder.OwnsMany(r => r.SubRounds);
        });

        modelBuilder.Entity<OneLevelReview>().OwnsMany(x => x.SubRounds, ownedBuilder =>
        {
            ownedBuilder.ToJson();
        });
    }
}

Include stack traces

System.ArgumentException
  HResult=0x80070057
  Message=Expression of type 'System.Collections.Generic.IReadOnlyList`1[SubRound]' cannot be used for assignment to type 'System.Collections.Generic.List`1[SubRound]'
  Source=System.Linq.Expressions
  StackTrace:
   at System.Linq.Expressions.Expression.Assign(Expression left, Expression right)
   at Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions.Assign(MemberExpression memberExpression, Expression valueExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.CreateJsonShapers(IEntityType entityType, Boolean nullable, ParameterExpression jsonReaderDataParameter, ParameterExpression keyValuesParameter, Expression parentEntityExpression, INavigation navigation)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitExtension(Expression extensionExpression)
   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 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.Internal.InternalDbSet`1.System.Collections.Generic.IEnumerable<TEntity>.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

Include provider and version information

EF Core version: 8.0.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 8

@maumar maumar assigned maumar and unassigned maumar Feb 10, 2024
@maumar
Copy link
Contributor

maumar commented Feb 10, 2024

note: code works fine when _rounds, _subRounds and _subRounds are defined as readonly fields

private readonly List<ReviewRound> _rounds = [];

@bperc2
Copy link
Author

bperc2 commented Feb 12, 2024

I appreciate the quick response. Making my list fields readonly will work fine.

@maumar
Copy link
Contributor

maumar commented Feb 16, 2024

when building MaterializeJsonEntityCollection method call, we take navigation.ClrType as target collection type. In reality the logic is more complicated. We should do something like navigation.GetMemberInfo(...).GetMemberType() instead.

maumar added a commit that referenced this issue Feb 16, 2024
…ing owned entities mapped with .ToJson()

when building MaterializeJsonEntityCollection method call, we were using navigation.ClrType as target collection type. However in case of navigation mapped to a private field, where the public property type doesn't match the backing field, we need more sophisticated approach.
Fix is to use navigation.GetMemberInfo(...).GetMemberType() which correctly returns the type of a backing field in that case, so we don't have a type mismatch when trying to assign MaterializeJsonEntityCollection to the field.

Fixes #33046
@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 Feb 16, 2024
@maumar maumar added this to the 9.0.0 milestone Feb 16, 2024
maumar added a commit that referenced this issue Feb 18, 2024
Fix to #33046 - ArgumentException thrown when building queries involving owned entities mapped with .ToJson()
@ajcvickers ajcvickers modified the milestones: 9.0.0, 9.0.0-preview2 Feb 29, 2024
@roji roji modified the milestones: 9.0.0-preview2, 9.0.0 Oct 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-model-building area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Projects
None yet
4 participants