Skip to content

[SQL Server] Can't perform set operation over entity containing a complex JSON column #37726

@ArnaudB88

Description

@ArnaudB88

Bug description

Bug description

I am trying to union two entity lists of the same type and selecting some data (without consulting the complex entity type property), but it throws an exception:

The data type json cannot be used as an operand to the UNION, INTERSECT or EXCEPT operators because it is not comparable.

Why

As suggested in the EfCore 10 release notes, we are migrating from owned entities (saved as json) to complex entity types. This results in several problems, and the bug described here is one of them.
The described scenario worked fine with owned entities.

Scenario

We try to union two sets of entities of the same type, grouping and selecting a calculation. The complex entity type is not consulted in the query filter or projection.

Your code

//entities
public partial class Project
{
    public Guid Id { get; set; }
    public Guid? ParentProjectId { get; set; }

    [Ignore]
    public virtual Project ParentProject { get; set; }

    [Ignore]
    public virtual ICollection<Project> ParentProjects { get; set; }

    [Ignore]
    public virtual ICollection<ProjectOrganisatie> ProjectOrganisaties { get; set; }

    public IList<KolomMetadata> KolomMetadata { get; set; }
}
public partial class ProjectOrganisatie
{
    public Guid Id { get; set; }
    public Guid ProjectId { get; set; }
    public bool IsInactief { get; set; }
	
    [Ignore]
    public virtual Project Project { get; set; }
}

//Mappings
public partial class ProjectMap
    : IEntityTypeConfiguration<ClientName.ProjectName.Domain.Entities.Project>
{
    public void Configure(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<ClientName.ProjectName.Domain.Entities.Project> builder)
    {
        builder.ToTable("Project", "dbo");
        builder.HasKey(t => t.Id);
		
        builder.Property(t => t.ParentProjectId)
            .HasColumnName("ParentProjectId")
            .HasColumnType("uniqueidentifier");

        builder.HasOne(t => t.ParentProject)
            .WithMany(t => t.ParentProjects)
            .HasForeignKey(d => d.ParentProjectId)
            .HasConstraintName("FK_Project_Project");

        builder.ComplexCollection(t => t.KolomMetadata, builder => builder.ToJson().Configure());		
		//Note: the Configure() method sets the property names of the KolomMetadata to camelCase
	}
}
public partial class ProjectOrganisatieMap
    : IEntityTypeConfiguration<ClientName.ProjectName.Domain.Entities.ProjectOrganisatie>
{
    public void Configure(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<ClientName.ProjectName.Domain.Entities.ProjectOrganisatie> builder)
    {
        builder.ToTable("ProjectOrganisatie", "dbo");
        builder.HasKey(t => t.Id);
	
        builder.Property(t => t.ProjectId)
            .IsRequired()
            .HasColumnName("ProjectId")
            .HasColumnType("uniqueidentifier");

        builder.Property(t => t.IsInactief)
            .IsRequired()
            .HasColumnName("IsInactief")
            .HasColumnType("bit")
            .HasDefaultValue(false);

        builder.HasOne(t => t.Project)
            .WithMany(t => t.ProjectOrganisaties)
            .HasForeignKey(d => d.ProjectId)
            .HasConstraintName("FK_ProjectOrganisatie_Project");

        builder.ComplexCollection(t => t.KolomMetadata, builder => builder.ToJson().Configure());
	}
}

			
//Scenario
Guid projectId = Guid.NewGuid();
Expression<Func<T, bool>> filter = p => p.Id == projectId;
Expression<Func<T, TOfProperty>> projection = p => p.ProjectOrganisaties.Union(p.ParentProjects.SelectMany(p2 => p2.ProjectOrganisaties))
    .GroupBy(po => po.Project.ParentProjectId ?? po.ProjectId)
    .Select(g => new
    {
        AantalInactief = g.Count(po => po.IsInactief)
    })
    .FirstOrDefault();

var query = dbContext.Set<Project>.AsQueryable()
  .Where(filter)
  .AsNoTracking() //read only query
  .Select(projection);
  
var expression = query.Expression; //see below
var result = query.FirstOrDefaultAsync()(); //Throws exception


//Expression:
{[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].TagWith("Method: EntityFrameworkGenericRepositoryTests.FirstOrDefaultWithProjectionAsync_Should_Work_WithUnion() at line 61").Where(p => (p.Id == value(ClientName.ProjectName.Tests.RepositoryTests.EntityFrameworkGenericRepositoryTests+<>c__DisplayClass1_0).project.Id)).AsNoTracking().Select(p => p.ProjectOrganisaties.Union(p.ParentProjects.SelectMany(p2 => p2.ProjectOrganisaties)).GroupBy(po => (po.Project.ParentProjectId ?? po.ProjectId)).Select(g => new <>f__AnonymousType5`1(AantalInactief = g.Count(po => po.IsInactief))).FirstOrDefault())}
    Arguments: Count = 2
    CanReduce: false
    DebugView: ".Call System.Linq.Queryable.Select(\r\n    .Call Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.AsNoTracking(.Call System.Linq.Queryable.Where(\r\n            .Call Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.TagWith(\r\n                .Extension<Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression>,\r\n                \"Method: EntityFrameworkGenericRepositoryTests.FirstOrDefaultWithProjectionAsync_Should_Work_WithUnion() at line 61\")\r\n            ,\r\n            '(.Lambda #Lambda1<System.Func`2[ClientName.ProjectName.Domain.Entities.Project,System.Boolean]>))),\r\n    '(.Lambda #Lambda2<System.Func`2[ClientName.ProjectName.Domain.Entities.Project,<>f__AnonymousType5`1[System.Int32]]>))\r\n\r\n.Lambda #Lambda1<System.Func`2[ClientName.ProjectName.Domain.Entities.Project,System.Boolean]>(ClientName.ProjectName.Domain.Entities.Project $p)\r\n{\r\n    $p.Id == (.Constant<ClientName.ProjectName.Tests.RepositoryTests.EntityFrameworkGenericRepositoryTests+<>c__DisplayClass1_0>(Inte
rreg.ProjectName.Tests.RepositoryTests.Entit..."
    Method: {System.Linq.IQueryable`1[<>f__AnonymousType5`1[System.Int32]] Select[Project,<>f__AnonymousType5`1](System.Linq.IQueryable`1[ClientName.ProjectName.Domain.Entities.Project], System.Linq.Expressions.Expression`1[System.Func`2[ClientName.ProjectName.Domain.Entities.Project,<>f__AnonymousType5`1[System.Int32]]])}
    NodeType: Call
    Object: null
    Type: {Name = "IQueryable`1" FullName = "System.Linq.IQueryable`1[[<>f__AnonymousType5`1[[System.Int32, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], ClientName.ProjectName.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"}

Stack traces

Microsoft.Data.SqlClient.SqlException
  HResult=0x80131904
  Message=The data type json cannot be used as an operand to the UNION, INTERSECT or EXCEPT operators because it is not comparable.
  Source=Core Microsoft SqlClient Data Provider
  StackTrace:
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__195_0(Task`1 result)
   at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.<ExecuteReaderAsync>d__22.MoveNext()
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.<ExecuteReaderAsync>d__22.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<InitializeReaderAsync>d__21.MoveNext()
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.<ExecuteAsync>d__7`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<MoveNextAsync>d__20.MoveNext()
   at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1.ConfiguredValueTaskAwaiter.GetResult()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleOrDefaultAsync>d__16`1.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleOrDefaultAsync>d__16`1.MoveNext()
   at ClientName.ProjectName.Tests.RepositoryTests.EntityFrameworkGenericRepositoryTests.<FirstOrDefaultWithProjectionAsync_Should_Work_WithUnion>d__1.MoveNext() in C:\Projects\ClientName\ProjectName\ClientName.ProjectName.Tests\RepositoryTests\EntityFrameworkGenericRepositoryTests.cs:line 61
   at Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.<ExecuteInternalAsync>d__55.MoveNext()

Verbose output


EF Core version

10.0.3

Database provider

Microsoft.EntityFrameworkCore.SqlServer

Target framework

.NET 10

Operating system

Windows 11

IDE

Visual Studio 2026 18.2.1

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions