Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TPT: Base type key field name is taken from derived type #23438

Closed
oleneveu opened this issue Nov 23, 2020 · 2 comments
Closed

TPT: Base type key field name is taken from derived type #23438

oleneveu opened this issue Nov 23, 2020 · 2 comments

Comments

@oleneveu
Copy link

oleneveu commented Nov 23, 2020

Context

I have to map an existing database to EFC5, and I want to take advantage of the Table-Per-Type feature.
Consider the following two tables, paying attention to the names of the PK fields:

CREATE TABLE [Identity](
	[IdentityId] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nvarchar](64) NULL,
	-- (...)
	CONSTRAINT [PK_Identity] PRIMARY KEY CLUSTERED ( [IdentityId] ASC )
) ON [PRIMARY]
GO

CREATE TABLE [User](
	[UserId] [int] NOT NULL,
	[Email] [nvarchar](100) NOT NULL,
	-- (...)
	CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ( [UserId] ASC )
)
GO

ALTER TABLE [dbo].[User]  WITH CHECK ADD  CONSTRAINT [Ctr_User_UserId] FOREIGN KEY([UserId])
REFERENCES [dbo].[Identity] ([IdentityId])
GO

They are mapped to these entities:

    public partial class Identity
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
        // (...)
    }

    public partial class User : Identity
    {
        public string Email { get; set; }
        // (...)
    }

The column names are declared here:

            modelBuilder.Entity<Identity>(entity =>
            {
                entity.Property(e => e.Id).HasColumnName("IdentityId");
            });

            modelBuilder.Entity<User>(entity =>
            {
                entity.Property(u => u.Id).HasColumnName("UserId");
            });

Issue

EFC5 considers that the actual PK field name for the table 'Identity' is 'UserId' instead of 'IdentityId' as declared.
Any query will fail, and that behavior can be exposed when generating a migration:

            migrationBuilder.CreateTable(
                name: "Identity",
                columns: table => new
                {
                    UserId = table.Column<int>(type: "int", nullable: false) // !!! Should be 'IdentityId' !!!
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Name = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
                    // (...)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Identity", x => x.UserId);
                });

            migrationBuilder.CreateTable(
                name: "User",
                columns: table => new
                {
                    UserId = table.Column<int>(type: "int", nullable: false),
                    Email = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
                    // (...)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_User", x => x.UserId);
                });

This database was actually previously mapped to an EF6 model, using the TPT feature for these tables.

Provider and version information

EF Core version: 5.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 5.0
Operating system: Windows 10.0.19041.572
IDE: Visual Studio 2019 16.8.2

@oleneveu
Copy link
Author

Here is a small project that reproduces the issue.
Just add a migration to see the generated model.

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

namespace EFCoreBug
{
    class Program
    {
        class TestDbContext : DbContext
        {
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;initial catalog=EFC5_TPT;Integrated Security=true");
            }

            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
                modelBuilder.Entity<Identity>().Property(u => u.Id).HasColumnName("IdentityId");
                modelBuilder.Entity<User>().Property(u => u.Id).HasColumnName("UserId");

            }

            public DbSet<Identity> Identities { get; set; }
            public DbSet<User> Users { get; set; }
        }

        [Table("Identity")]
        class Identity
        {
            [Key]
            public int Id { get; set; }
            public string Name { get; set; }
        }

        [Table("User")]
        class User : Identity
        {
            public string Email { get; set; }
        }

        static void Main(string[] args)
        {
        }
    }
}

Migration:

        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Identity",
                columns: table => new
                {
                    UserId = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Name = table.Column<string>(type: "nvarchar(max)", nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Identity", x => x.UserId);
                });

            migrationBuilder.CreateTable(
                name: "User",
                columns: table => new
                {
                    UserId = table.Column<int>(type: "int", nullable: false),
                    Email = table.Column<string>(type: "nvarchar(max)", nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_User", x => x.UserId);
                    table.ForeignKey(
                        name: "FK_User_Identity_UserId",
                        column: x => x.UserId,
                        principalTable: "Identity",
                        principalColumn: "UserId",
                        onDelete: ReferentialAction.Restrict);
                });
        }

@ajcvickers
Copy link
Member

Duplicate of #19811

@ajcvickers ajcvickers marked this as a duplicate of #19811 Nov 23, 2020
@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants