Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 3, 2025

  • I've read the guidelines for contributing and seen the walkthrough
  • I've posted a comment on an issue with a detailed description of how I am planning to contribute and got approval from a member of the team
  • The code builds and tests pass locally (also verified by our automated build checks)
  • Commit messages follow this format:
        Summary of the changes
        - Detail 1
        - Detail 2

        Fixes #bugnumber
  • Tests for the changes have been added (for bug fixes / features)
  • Code follows the same patterns and style as existing code in this repo

Description

TableSharingConcurrencyTokenConvention incorrectly creates shadow concurrency token properties on JSON-mapped owned entities, causing model validation to fail with:

Property 'OwnedEntity._TableSharingConcurrencyTokenConvention_RowVersion' cannot have both a column name ('RowVersion') and a JSON property name configured.

Repro:

modelBuilder.Entity<BaseEntity>(b =>
{
    b.Property(e => e.RowVersion).IsRowVersion();
    b.HasDiscriminator<string>("Type").HasValue<DerivedEntity>(nameof(DerivedEntity));
});

modelBuilder.Entity<DerivedEntity>(b =>
{
    b.OwnsOne(x => x.Owned, ob => ob.ToJson());
});

Changes

  • TableSharingConcurrencyTokenConvention.cs: Skip IsMappedToJson() entity types when building table-to-entity mappings, consistent with ValidateSharedTableCompatibility
  • TableSharingConcurrencyTokenConventionTest.cs: Add tests for JSON-mapped owned entities with concurrency tokens

Fixes #36614

Original prompt

This section details on the original issue you should resolve

<issue_title>Conflict with RowVersion/Concurrency Token in TPH with Owned Entity as JSON (ValidateJsonProperties throws exception)</issue_title>
<issue_description>### Bug description

After upgrading from EF Core 9.x to 10.0.0, an exception is thrown during model building (e.g., EnsureCreated or during migrations) when the following combination is used:

  • TPH inheritance (base class + derived class)
  • A traditional RowVersion column in the base class (IsRowVersion / IsConcurrencyToken)
  • A derived class containing an owned type that is mapped to JSON using ToJson()

This model works correctly in EF Core 9. Starting with EF Core 10, it fails with the following exception:

System.InvalidOperationException: Property 'OwnedEntity._TableSharingConcurrencyTokenConvention_RowVersion' cannot have both a column name ('RowVersion') and a JSON property name ('_TableSharingConcurrencyTokenConvention_RowVersion') configured. Properties in JSON-mapped types should use JSON property names, not column names.

Expected Behavior

The model should be built without throwing an exception.
The RowVersion in the base class should work as a standard timestamp/rowversion column, and the owned JSON object should be validated independently without inheriting concurrency metadata.

Actual Behavior

During model creation (dbContext.Database.EnsureCreated()), EF Core throws an InvalidOperationException (full stack trace included in the sample project).
This prevents both model creation and migrations.

Issue.zip

Your code

// Sample Entities

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

    public string Name { get; set; } = string.Empty;

    public long RowVersion { get; set; }
}

public class DerivedEntity : BaseEntity
{
    public OwnedEntity Owned { get; set; } = new();
}

public class OwnedEntity
{
    public string Description { get; set; } = "Any";
}

// DB Context --> leads to the error on EF Core 10
public class MyDbContext(DbContextOptions<MyDbContext> dbContextOptions) : DbContext(dbContextOptions)
{
    protected override void OnModelCreating(
        ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BaseEntity>(b =>
        {
            b.HasKey(e => e.Id);
            
            b.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken();

            b.Property(e => e.Name).HasMaxLength(100).IsRequired();

            b.HasDiscriminator<string>("Type")
                .HasValue<DerivedEntity>(nameof(DerivedEntity));
        });

        modelBuilder.Entity<DerivedEntity>(b =>
        {
            b.OwnsOne(x => x.Owned, ob =>
            {
                ob.ToJson();
                ob.Property(o => o.Description).HasMaxLength(200).IsRequired();
            });
        });
    }
}

// DB Context --> workaround
public class MyDbContext(DbContextOptions<MyDbContext> dbContextOptions) : DbContext(dbContextOptions)
{
    protected override void OnModelCreating(
        ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BaseEntity>(b =>
        {
            b.HasKey(e => e.Id);
            
            //b.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken();
            b.Ignore(x => x.RowVersion);

            b.Property(e => e.Name).HasMaxLength(100).IsRequired();

            b.HasDiscriminator<string>("Type")
                .HasValue<DerivedEntity>(nameof(DerivedEntity));
        });

        modelBuilder.Entity<DerivedEntity>(b =>
        {
            // Moved from base entity to derived entity
            b.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken();
            
            b.OwnsOne(x => x.Owned, ob =>
            {
                ob.ToJson();
                ob.Property(o => o.Description).HasMaxLength(200).IsRequired();
            });
        });
    }
}

Stack traces

Unhandled exception. System.InvalidOperationException: Property 'OwnedEntity._TableSharingConcurrencyTokenConvention_RowVersion' cannot have both a column name ('RowVersion') and a JSON property name ('_TableSharingConcurrencyTokenConvention_RowVersion') configured. Properties in JSON-mapped types should use JSON property names, not column names.
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.ValidateJsonProperties(IConventionTypeBase typeBase)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.ValidateJsonEntityProperties(StoreObjectIdentifier& storeObject, IEntityType jsonEntityType)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.ValidateJsonEntities(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal.SqlServerMod...

</details>

- Fixes dotnet/efcore#37274

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

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

Copilot AI changed the title [WIP] Fix conflict with RowVersion in TPH with owned entity JSON Fix TableSharingConcurrencyTokenConvention to skip JSON-mapped entity types Dec 3, 2025
Copilot finished work on behalf of AndriySvyryd December 3, 2025 01:33
Copilot AI requested a review from AndriySvyryd December 3, 2025 01:33
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.

"The entity type requires a primary key to be defined" with cross-schema FKs and strongly-typed jsonb mapping

2 participants