Skip to content

Fix duplicate JSON column in TPT child tables for complex types declared on base entity type#37958

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/fix-duplicate-json-column-child-table
Draft

Fix duplicate JSON column in TPT child tables for complex types declared on base entity type#37958
Copilot wants to merge 2 commits intomainfrom
copilot/fix-duplicate-json-column-child-table

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 19, 2026

In TPT inheritance, a complex property configured with .ToJson() on a base entity type incorrectly produced a JSON column in every child table in addition to the base table — causing duplicate schema and redundant data writes on insert.

modelBuilder.Entity<Person>()
    .UseTptMappingStrategy()
    .ComplexProperty(a => a.Address, b => b.ToJson());
// Bug: "Address" JSON column was created in both Person, Students, and Employees tables
// Fix: "Address" JSON column is created only in the Person (base) table

Root cause

RelationalModel.CreateTableMapping() iterated mappedType.GetComplexProperties(), which returns inherited properties. Scalar properties are filtered by GetColumnName(mappedTable) == null, but no equivalent filter existed for JSON-mapped complex properties, so the column was unconditionally created on every table in the hierarchy.

Changes

  • RelationalModel.CreateTableMapping() — when processing complex properties for an entity type, skip any complex property whose declaring entity type maps to a different table (the base table in TPT). The check is bypassed for TPC, where each concrete table must contain all columns including inherited ones.
  • RelationalModelTest — added Complex_property_json_column_is_not_duplicated_in_TPT_child_tables to assert the JSON column appears only in the base table for a TPT hierarchy.
Original prompt

This section details on the original issue you should resolve

<issue_title>EF Core 11.0: Complex types and JSON Columns on Entity Types with TPT creates Duplicate JSON Column in Child Table</issue_title>
<issue_description>### Bug description

EF Core 11.0 introduced Complex types and JSON columns on entity types with TPT/TPC inheritance.

Expected

When using TPT, a JSON column is created in Child Table in addition to Parent table.

Image

Actual

When using TPT, a JSON column created only in the Parent table.

Your code

Using EF Core 11,

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net11.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="11.0.0-preview.2.26159.112">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="11.0.0-preview.2.26159.112" />
  </ItemGroup>

</Project>

Consider the following Db Context

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.ComponentModel.DataAnnotations.Schema;

using var context = new MyDbContext();

context.Database.EnsureDeleted();
context.Database.EnsureCreated();

await context.Students.AddRangeAsync(
    [
        new Student
        {
            Name = "John Doe",
            School = "School A",
            Address = new Address
            {
                AddressLine1 = "123 Main St",
                City = "Redmond",
                State = "WA"
            }
        }
    ]);


await context.Employees.AddRangeAsync(
    [
        new Employee
        {
            Name = "Jane Doe",
            Employer = "Employer A",
            Address = new Address
            {
                AddressLine1 = "456 Elm St",
                City = "Othertown",
                State = "NY"
            }
        }
    ]);

await context.SaveChangesAsync();

public abstract class Person
{
    public int Id { get; set; }

    public required string Name { get; init; }

    public required Address Address { get; set; }
}

public class Student : Person
{
    public required string School { get; set; }
}

public class Employee : Person
{
    public required string Employer { get; set; }
}

[ComplexType]
public class Address
{
    public required string AddressLine1 { get; set; }

    public required string City { get; set; }

    public required string State { get; set; }
}

public class MyDbContext : DbContext
{
    public DbSet<Student> Students { get; set; }

    public DbSet<Employee> Employees { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer(@"<ConnectionString>") //  Database compatibility level 170 (Microsoft SQL Server 2025)
            .LogTo(Console.WriteLine, LogLevel.Information);
    }

    override protected void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>()
            .UseTptMappingStrategy()
            .ComplexProperty(a => a.Address, b => b.ToJson());
    }
}

Stack traces

N/A

Verbose output

N/A

EF Core version

11.0.0-preview.2.26159.112

Database provider

Microsoft.EntityFrameworkCore.SqlServer

Target framework

.NET 11

Operating system

Windows 11

IDE

Visual Studio 2026 Insiders [11605.296]</issue_description>

Comments on the Issue (you are @copilot in this section)

@roji # AI Triage

The below is an AI-generated analysis and may contain inaccuracies.

Classification

Type: Bug
Areas: area-complex-types, area-model-building

Analysis

I was able to confirm a bug with complex types mapped to JSON via .ToJson() on a base entity type using TPT inheritance. The JSON column is incorrectly duplicated in child tables.

When running the reported scenario (tested on SQLite with EF 11.0 preview 2), the following table DDL is generated:

CREATE TABLE "Person" (
    "Id" INTEGER NOT NULL CONSTRAINT "PK_Person" PRIMARY KEY,
    "Name" TEXT NOT NULL,
    "Address" TEXT NOT NULL   -- correct: JSON column in base table
);

CREATE TABLE "Employees" (
    "Id" INTEGER NOT NULL CONSTRAINT "PK_Employees" PRIMARY KEY,
    "Employer" TEXT NOT NULL,
    "Address" TEXT NOT NULL,  -- BUG: duplicate JSON ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes dotnet/efcore#37937

<!-- START COPILOT CODING AGENT TIPS -->
---

📍 Connect Copilot coding agent with [Jira](https://gh.io/cca-jira-docs), [Azure Boards](https://gh.io/cca-azure-boards-docs) or [Linear](https://gh.io/cca-linear-docs) to delegate work to Copilot in one click without leaving your project management tool.

…red on base entity type

When a complex property is mapped to JSON on a base entity type in a TPT
hierarchy, the JSON column was being incorrectly created in child tables
in addition to the base table. This is because `GetComplexProperties()`
returns inherited properties, and there was no equivalent of the
`GetColumnName(mappedTable) == null` filter used for scalar properties.

The fix adds a check in `CreateTableMapping` to skip complex properties
declared on base entity types that map to a different table (TPT scenario).
TPC is excluded from this check since each TPC concrete table needs all
properties including inherited ones.

Fixes #38535 (Complex types and JSON Columns on Entity Types with TPT
creates Duplicate JSON Column in Child Table)

Co-authored-by: roji <1862641+roji@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix duplicate JSON column in child table for TPT Fix duplicate JSON column in TPT child tables for complex types declared on base entity type Mar 19, 2026
Copilot AI requested a review from roji March 19, 2026 17:15
Copilot finished work on behalf of roji March 19, 2026 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants