ComplexCollection Entity splitting support
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
### Bug description
When using a ComplexCollection (JSON) mapping on an entity that is split across multiple tables using `SplitToTable`, queries that include the complex collection fail with:
`System.InvalidOperationException: Sequence contains more than one element`
Additional issues observed:
- Despite using Table splitting, the framework attempts to write data for the "Tags" collection to both tables
- The `[Column("tags")]` attribute is ignored and the C# property name "Tags" is used instead for column mapping
### Your code
```csharp
#:package Microsoft.EntityFrameworkCore@10.0.5
#:package Microsoft.EntityFrameworkCore.Sqlite@10.0.5
#:property PublishAot=false
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.ComponentModel.DataAnnotations.Schema;
await using var conn = new SqliteConnection("Data Source=:memory:");
conn.Open();
var options = new DbContextOptionsBuilder()
.UseSqlite(conn)
.EnableDetailedErrors()
.LogTo(Console.WriteLine, LogLevel.Information)
.Options;
await using (var ctx = new AppDbContext(options))
{
ctx.Database.EnsureCreated();
var post = new Post
{
Title = "Hello",
IsFeatured = true,
Tags =
[
new Tag { Name = "a" },
new Tag { Name = "b" }
]
};
ctx.Posts.Add(post);
await ctx.SaveChangesAsync();
}
await using (var ctx = new AppDbContext(options))
{
var data = await ctx.Posts
.AsNoTracking()
.Where(p => p.Tags.Any(t => t.Name.ToLower().Contains("a")))
.ToListAsync();
Console.WriteLine($"Posts count: {data.Count}");
}
[Table("posts")]
class Post
{
[Column("id")]
public int Id { get; set; }
[Column("title")]
public string? Title { get; set; }
[Column("is_featured")]
public bool IsFeatured { get; set; }
[Column("tags")]
public List Tags { get; set; } = [];
}
class Tag
{
public string Name { get; set; } = string.Empty;
}
class AppDbContext(DbContextOptions options) : DbContext(options)
{
public DbSet Posts => Set();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity().HasKey(p => p.Id);
modelBuilder.Entity().ComplexCollection(p => p.Tags, c => c.ToJson());
modelBuilder.Entity().SplitToTable("posts_extra", tb =>
{
tb.Property(p => p.IsFeatured);
});
}
}
```
### Stack traces
```text
Unhandled exception. System.InvalidOperationException: Sequence contains more than one element
at System.Linq.ThrowHelper.ThrowMoreThanOneElementException()
at System.Linq.Enumerable.TryGetSingle[TSource](IEnumerable`1 source, Boolean& found)
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.GenerateJsonReader(Int32 jsonColumnIndex, ITypeBase structuralType)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ProcessTopLevelComplexJsonProperties(StructuralTypeShaperExpression shaper, ParameterExpression instanceVariable, List`1 expressions)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.AddStructuralTypeInitialization(StructuralTypeShaperExpression shaper, ParameterExpression instanceVariable, List`1 variables, List`1 expressions)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.StructuralTypeMaterializerInjector.MaterializeEntity(StructuralTypeShaperExpression shaper, ParameterExpression materializationContextVariable, ParameterExpression concreteEntityTypeVariable, ParameterExpression instanceVariable, ParameterExpression entryVariable)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.StructuralTypeMaterializerInjector.ProcessStructuralTypeShaper(StructuralTypeShaperExpression shaper)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.StructuralTypeMaterializerInjector.VisitExtension(Expression extensionExpression)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.StructuralTypeMaterializerInjector.Inject(Expression expression)
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.InjectStructuralTypeMaterializers(Expression expression)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitExtension(Expression extensionExpression)
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.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 Program.$(String[] args)
at Program.$(String[] args)
at Program.$(String[] args)
at Program.(String[] args)
```
### Verbose output
```text
```
### EF Core version
10.0.5
### Database provider
Npgsql.EntityFrameworkCore.PostgreSQL, Microsoft.EntityFrameworkCore.Sqlite
### Target framework
.NET 10.0
### Operating system
_No response_
### IDE
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.