Skip to content

Commit

Permalink
Fix to #33046 - ArgumentException thrown when building queries involv…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
maumar committed Feb 16, 2024
1 parent a418545 commit 8267b6d
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,8 @@ private Expression CreateJsonShapers(
if (ownedNavigation.IsCollection)
{
var shaperEntityParameter = Parameter(ownedNavigation.DeclaringEntityType.ClrType);
var shaperCollectionParameter = Parameter(ownedNavigation.ClrType);
var ownedNavigationType = ownedNavigation.GetMemberInfo(forMaterialization: true, forSet: true).GetMemberType();
var shaperCollectionParameter = Parameter(ownedNavigationType);
var expressions = new List<Expression>();
var expressionsForTracking = new List<Expression>();

Expand Down Expand Up @@ -1484,11 +1485,12 @@ private Expression CreateJsonShapers(

if (navigation is { IsCollection: true })
{
var collectionClrType = navigation.GetMemberInfo(forMaterialization: true, forSet: true).GetMemberType();
var materializeJsonEntityCollectionMethodCall =
Call(
MaterializeJsonEntityCollectionMethodInfo.MakeGenericMethod(
navigation.TargetEntityType.ClrType,
navigation.ClrType),
collectionClrType),
QueryCompilationContext.QueryContextParameter,
keyValuesParameter,
jsonReaderDataParameter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,61 @@ public class JsonFieldOnly32939

#endregion

#region 33046

protected abstract void Seed33046(Context33046 ctx);

[ConditionalFact]
public virtual async Task Query_with_nested_json_collection_mapped_to_private_field_via_IReadOnlyList()
{
var contextFactory = await InitializeAsync<Context33046>(seed: Seed33046);
using var context = contextFactory.CreateContext();
var query = context.Reviews.ToList();
Assert.Equal(1, query.Count);
}

protected class Context33046(DbContextOptions options) : DbContext(options)
{
public DbSet<Review> Reviews { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Review>().Property(x => x.Id).ValueGeneratedNever();
modelBuilder.Entity<Review>().OwnsMany(x => x.Rounds, ownedBuilder =>
{
ownedBuilder.ToJson();
ownedBuilder.OwnsMany(r => r.SubRounds);
});
}

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

#pragma warning disable IDE0044 // Add readonly modifier
private List<ReviewRound> _rounds = [];
#pragma warning restore IDE0044 // Add readonly modifier
public IReadOnlyList<ReviewRound> Rounds => _rounds.AsReadOnly();
}

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

#pragma warning disable IDE0044 // Add readonly modifier
private List<SubRound> _subRounds = [];
#pragma warning restore IDE0044 // Add readonly modifier
public IReadOnlyList<SubRound> SubRounds => _subRounds.AsReadOnly();
}

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

#endregion

#region ArrayOfPrimitives

[ConditionalTheory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ protected override void Seed30028(MyContext30028 ctx)
N'{{""RootName"":""e4"",""Collection"":[{{""BranchName"":""e4 c1"",""Nested"":{{""LeafName"":""e4 c1 l""}}}},{{""BranchName"":""e4 c2"",""Nested"":{{""LeafName"":""e4 c2 l""}}}}],""OptionalReference"":{{""BranchName"":""e4 or"",""Nested"":{{""LeafName"":""e4 or l""}}}}}}')");
}

protected override void Seed33046(Context33046 ctx)
=> ctx.Database.ExecuteSqlRaw(
@"INSERT INTO [Reviews] ([Rounds], [Id])
VALUES(N'[{{""RoundNumber"":11,""SubRounds"":[{{""SubRoundNumber"":111}},{{""SubRoundNumber"":112}}]}}]', 1)");

protected override void SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx)
{
var entity1 = new MyEntityArrayOfPrimitives
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ protected override void Seed30028(MyContext30028 ctx)
'{{""RootName"":""e4"",""Collection"":[{{""BranchName"":""e4 c1"",""Nested"":{{""LeafName"":""e4 c1 l""}}}},{{""BranchName"":""e4 c2"",""Nested"":{{""LeafName"":""e4 c2 l""}}}}],""OptionalReference"":{{""BranchName"":""e4 or"",""Nested"":{{""LeafName"":""e4 or l""}}}}}}')");
}

protected override void Seed33046(Context33046 ctx)
=> ctx.Database.ExecuteSqlRaw(
@"INSERT INTO ""Reviews"" (""Rounds"", ""Id"")
VALUES('[{{""RoundNumber"":11,""SubRounds"":[{{""SubRoundNumber"":111}},{{""SubRoundNumber"":112}}]}}]', 1)");

protected override void SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx)
{
var entity1 = new MyEntityArrayOfPrimitives
Expand Down

0 comments on commit 8267b6d

Please sign in to comment.