Skip to content

Regression: LINQ Select with struct ComplexProperty + collection navigation throws InvalidOperationException #37926

@AwkEng

Description

@AwkEng

Bug description

I ran into a regression upgrading from EF Core 11 Preview 1 to Preview 2. In full transparency, I had Claude produce a minimal repro.

https://github.com/AwkEng/EfCoreComplexPropertyBug

Upgrading from EF Core 11.0.0-preview.1 to 11.0.0-preview.2 introduces a regression when a LINQ Select projection combines:

  1. A collection navigation whose elements have a readonly record struct ComplexProperty projected via constructor
  2. A struct ComplexProperty with nested struct ComplexProperties projected via constructor on the same entity

Either projection works individually. When combined in the same Select, EF Core throws:

InvalidOperationException: An expression of type 'Outer' cannot be used to initialize an array of type 'System.Object'

The stack trace points to ShaperProcessingExpressionVisitor.ProcessProjection calling LiftComplexTypeToNewArray, which appears new in preview.2 and does not box the struct before adding it to the object[] array initializer expression.

Reproduces on both SQLite and PostgreSQL — this is an EF Core core issue, not provider-specific.

Your code

#:package Microsoft.EntityFrameworkCore@11.0.0-preview.2.26159.112
#:package Microsoft.EntityFrameworkCore.Sqlite@11.0.0-preview.2.26159.112

using Microsoft.EntityFrameworkCore;

var options = new DbContextOptionsBuilder<AppDbContext>()
    .UseSqlite("Data Source=:memory:")
    .Options;

using var db = new AppDbContext(options);
db.Database.OpenConnection();
db.Database.EnsureCreated();

var parentId = Guid.NewGuid();
db.Parents.Add(new Parent
{
    Id = parentId,
    Name = "P1",
    Coords = new Outer
    {
        First = new Inner { A = "1", B = "2" },
        Second = new Inner { A = "3", B = "4" }
    },
    Children = [new Child { Id = Guid.NewGuid(), ParentId = parentId, Data = new Inner { A = "x", B = "y" } }]
});
db.SaveChanges();

// Works in preview.1, throws in preview.2
var result = await db.Parents
    .AsNoTracking()
    .Where(p => p.Id == parentId)
    .Select(p => new ParentVm(
        p.Name,
        p.Children.Select(c => new ChildVm(new InnerVm(c.Data))).ToList(),  // collection nav + struct
        new OuterVm(p.Coords)                                               // nested struct
    ))
    .FirstOrDefaultAsync();

Console.WriteLine(result);

// ── Domain ──────────────────────────────────────────────────────────

public readonly record struct Inner
{
    public string? A { get; init; }
    public string? B { get; init; }
}

public readonly record struct Outer
{
    public Inner First { get; init; }
    public Inner Second { get; init; }
}

public class Child
{
    public Guid Id { get; set; }
    public Guid ParentId { get; set; }
    public Inner Data { get; set; }
    public Parent? Parent { get; set; }
}

public class Parent
{
    public Guid Id { get; set; }
    public string Name { get; set; } = default!;
    public Outer Coords { get; set; }
    public List<Child> Children { get; set; } = [];
}

// ── View models ─────────────────────────────────────────────────────

public record InnerVm(string? A, string? B)
{
    public InnerVm(Inner i) : this(i.A, i.B) { }
}

public record OuterVm(InnerVm First, InnerVm Second)
{
    public OuterVm(Outer o) : this(new InnerVm(o.First), new InnerVm(o.Second)) { }
}

public record ChildVm(InnerVm Data);
public record ParentVm(string Name, List<ChildVm> Children, OuterVm Coords);

// ── DbContext ───────────────────────────────────────────────────────

public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
    public DbSet<Parent> Parents => Set<Parent>();
    public DbSet<Child> Children => Set<Child>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Parent>(e =>
        {
            e.HasKey(x => x.Id);
            e.ComplexProperty(x => x.Coords, c =>
            {
                c.ComplexProperty(x => x.First, f =>
                {
                    f.Property(x => x.A).HasColumnName("first_a");
                    f.Property(x => x.B).HasColumnName("first_b");
                });
                c.ComplexProperty(x => x.Second, s =>
                {
                    s.Property(x => x.A).HasColumnName("second_a");
                    s.Property(x => x.B).HasColumnName("second_b");
                });
            });
            e.HasMany(x => x.Children).WithOne(x => x.Parent).HasForeignKey(x => x.ParentId);
        });

        modelBuilder.Entity<Child>(e =>
        {
            e.HasKey(x => x.Id);
            e.ComplexProperty(x => x.Data, d =>
            {
                d.Property(x => x.A).HasColumnName("data_a");
                d.Property(x => x.B).HasColumnName("data_b");
            });
        });
    }
}

Stack traces

System.InvalidOperationException: An expression of type 'Outer' cannot be used to initialize an array of type 'System.Object'
   at System.Linq.Expressions.Expression.NewArrayInit(Type type, IEnumerable`1 initializers)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.<ProcessProjection>g__LiftComplexTypeToNewArray|19_1(Expression expression, Type newArrayType)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ProcessProjection(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ProcessProjection(Expression expression)
   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__DisplayClass11_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.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)

Verbose output


EF Core version

11.0.0-preview.2.26159.112 (regression from 11.0.0-preview.1.26104.118)

Database provider

Microsoft.EntityFrameworkCore.Sqlite 11.0.0-preview.2 + Npgsql.EntityFrameworkCore.PostgreSQL 11.0.0-preview.2

Target framework

.NET 11.0

Operating system

Windows 11

IDE

Visual Studio 2022

Metadata

Metadata

Assignees

Type

No fields configured for Bug.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions