efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Explicit configuration of a TPT relationship causes the database constraint not to be created

Open ajcvickers opened this issue 3 years ago • 0 comments

Consider the simple TPT model:

[Table("FeaturedPosts")]
public class FeaturedPost : Post
{
}

[Table("Posts")]
public class Post
{
    public int Id { get; set; }
    public string? Title { get; set; }
    public string? Content { get; set; }
}

This results in the following tables:

      CREATE TABLE [Posts] (
          [Id] int NOT NULL IDENTITY,
          [Title] nvarchar(max) NULL,
          [Content] nvarchar(max) NULL,
          CONSTRAINT [PK_Posts] PRIMARY KEY ([Id])
      );

      CREATE TABLE [FeaturedPosts] (
          [Id] int NOT NULL,
          CONSTRAINT [PK_FeaturedPosts] PRIMARY KEY ([Id]),
          CONSTRAINT [FK_FeaturedPosts_Posts_Id] FOREIGN KEY ([Id]) REFERENCES [Posts] ([Id]) ON DELETE CASCADE
      );

I want to change the cascade behavior for the FK constraint between the two tables, so I do this:

modelBuilder
    .Entity<Post>()
    .HasOne<FeaturedPost>()
    .WithOne()
    .HasForeignKey<FeaturedPost>(e => e.Id)
    .OnDelete(DeleteBehavior.ClientCascade);

Now the FK constraint disappears entirely!

      CREATE TABLE [FeaturedPosts] (
          [Id] int NOT NULL IDENTITY,
          CONSTRAINT [PK_FeaturedPosts] PRIMARY KEY ([Id])
      );

      CREATE TABLE [Posts] (
          [Id] int NOT NULL IDENTITY,
          [Title] nvarchar(max) NULL,
          [Content] nvarchar(max) NULL,
          CONSTRAINT [PK_Posts] PRIMARY KEY ([Id])
      );

Full code:

[Table("FeaturedPosts")]
public class FeaturedPost : Post
{
}

[Table("Posts")]
public class Post
{
    public int Id { get; set; }
    public string? Title { get; set; }
    public string? Content { get; set; }
}

public class SomeDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlServer(Your.ConnectionString)
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder
            .Entity<Post>()
            .HasOne<FeaturedPost>()
            .WithOne()
            .HasForeignKey<FeaturedPost>(e => e.Id)
            .OnDelete(DeleteBehavior.ClientCascade);
        
        modelBuilder.Entity<FeaturedPost>();
    }
}

public class Program
{
    public static void Main()
    {
        using (var context = new SomeDbContext())
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            context.Add(new Post());
            context.Add(new FeaturedPost());
            
            context.SaveChanges();
        }
        
        using (var context = new SomeDbContext())
        {
            foreach (var post in context.Set<Post>().ToList())
            {
                Console.WriteLine(post.GetType());
            }
        }
    }
}

ajcvickers avatar Sep 20 '22 18:09 ajcvickers