efcore
efcore copied to clipboard
Non-empty second migration in TPC scheme using inheritance
I'm using a bit change example from microsoft documentation "What's new in EF 7":
using Microsoft.EntityFrameworkCore;
using System.Reflection;
namespace EfCoreTpc;
public class Program
{
public static void Main(params string[] args)
{
IHost host = Host.CreateDefaultBuilder()
.ConfigureServices(services =>
{
services.AddDbContext<MyContext>(builder =>
{
builder.UseNpgsql("Host=localhost;Port=7435;Database=testdb;Username=admin;Password=testpass");
});
})
.Build();
host.Run();
}
}
public abstract class Animal
{
protected Animal()
{
}
protected Animal(string name)
{
Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
public abstract string Species { get; }
public Food? Food { get; set; }
}
public abstract class Pet : Animal
{
protected Pet(string name)
: base(name)
{
}
public string? Vet { get; set; }
public ICollection<Human> Humans { get; } = new List<Human>();
}
public class FarmAnimal : Animal
{
protected FarmAnimal()
: base()
{
}
public FarmAnimal(string name, string species)
: base(name)
{
Species = species;
}
public override string Species { get; }
[Precision(18, 2)]
public decimal Value { get; set; }
public override string ToString()
=> $"Farm animal '{Name}' ({Species}/{Id}) worth {Value:C} eats {Food?.ToString() ?? "<Unknown>"}";
}
public class Cat : Pet
{
public Cat(string name, string educationLevel)
: base(name)
{
EducationLevel = educationLevel;
}
public string EducationLevel { get; set; }
public override string Species => "Felis catus";
public override string ToString()
=> $"Cat '{Name}' ({Species}/{Id}) with education '{EducationLevel}' eats {Food?.ToString() ?? "<Unknown>"}";
}
public class Dog : Pet
{
public Dog(string name, string favoriteToy)
: base(name)
{
FavoriteToy = favoriteToy;
}
public string FavoriteToy { get; set; }
public override string Species => "Canis familiaris";
public override string ToString()
=> $"Dog '{Name}' ({Species}/{Id}) with favorite toy '{FavoriteToy}' eats {Food?.ToString() ?? "<Unknown>"}";
}
public class Human : Animal
{
public Human(string name)
: base(name)
{
}
public override string Species => "Homo sapiens";
public Animal? FavoriteAnimal { get; set; }
public ICollection<Pet> Pets { get; } = new List<Pet>();
public override string ToString()
=> $"Human '{Name}' ({Species}/{Id}) with favorite animal '{FavoriteAnimal?.Name ?? "<Unknown>"}'" +
$" eats {Food?.ToString() ?? "<Unknown>"}";
}
public class Food
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyContext : DbContext
{
public MyContext(DbContextOptions options)
: base(options)
{
}
public DbSet<Animal> Animals => Set<Animal>();
public DbSet<Pet> Pets => Set<Pet>();
public DbSet<FarmAnimal> FarmAnimals => Set<FarmAnimal>();
public DbSet<Cat> Cats => Set<Cat>();
public DbSet<Dog> Dogs => Set<Dog>();
public DbSet<Human> Humans => Set<Human>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Animal>().UseTpcMappingStrategy();
modelBuilder.Entity<Animal>(builder =>
{
builder
.Property(p => p.Id)
.UseHiLo();
builder
.Ignore(p => p.Food);
});
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
}
Then I create 2 migrations using:
- command "add-migration Init"
- command "add-migration t2" (immediatly after first migration)
The second migration must be empty. But I've got non-empty result:
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EfCoreTpc.Migrations
{
/// <inheritdoc />
public partial class t2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Pet");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Pet",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Vet = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Pet", x => x.Id);
});
}
}
}
Provider and version information
EF Core version: 7.0.1 Database provider: Npgsql Entity Framework Core provider for PostgreSQL Target framework: NET 7.0 Operating system: Windows 10 IDE: Visual Studio 2022 17.4
Note for triage: repros with SQL Server and on latest daily. Minimal repro below.
public class Program
{
public static void Main(params string[] args)
{
}
}
public abstract class Animal
{
public int Id { get; set; }
public string Name { get; set; }
}
public abstract class Pet : Animal
{
public string? Vet { get; set; }
public ICollection<Human> Humans { get; } = new List<Human>();
}
public class Cat : Pet
{
public string EducationLevel { get; set; }
}
public class Dog : Pet
{
public string FavoriteToy { get; set; }
}
public class Human : Animal
{
public Animal? FavoriteAnimal { get; set; }
public ICollection<Pet> Pets { get; } = new List<Pet>();
}
public class MyContext : DbContext
{
public DbSet<Animal> Animals => Set<Animal>();
public DbSet<Pet> Pets => Set<Pet>();
public DbSet<Cat> Cats => Set<Cat>();
public DbSet<Dog> Dogs => Set<Dog>();
public DbSet<Human> Humans => Set<Human>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Animal>().UseTpcMappingStrategy();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Data Source=(LocalDb)\MSSQLLocalDB;Database=AllTogetherNow")
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
}
}