如何阻止Entity Framework Core使用表-层次结构映射(TPH)?

编程语言 2026-07-10

我在使用MongoDB.EntityFrameworkCore

我有3个类:PodPlanetAsteroid,它们继承自SpaceObject

我想对PlanetAsteroid使用默认的表层次映射(TPH),把这些实体保存到一个SpaceObjects集合中,使用默认的判别器_t

但我想把Pod实体单独保存到它们自己的Pods集合中,因此在这种情况下只使用Table-Per-Type映射(TPT)。

我尝试在 Pod 上使用属性(特性)或在模型创建时指定集合:

[Collection("Pods")]
public class Pod : SpaceObject
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Pod>().ToCollection("Pods");
}

这不行:pods总是被保存到 SpaceObjects 集合中。

解决方案

你可以通过为需要使用TPH的实体引入一个中介基类来实现,同时把主基类保留给TPH/TPT继承使用。例如,我为将使用TPH判别器的项引入一个名为SpaceBody的基类:

    public abstract class SpaceObject
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set;  }
        public string Name { get; set; }
    }

    [Table("SpaceObjects")]
    public abstract class SpaceBody : SpaceObject
    {
    }

    public class Planet : SpaceBody
    {
        public const string Discriminator = "planet";
    }

    public class Asteroid : SpaceBody
    {
        public const string Discriminator = "rock";
    }

    [Table("Pods")]
    public class Pod : SpaceObject
    {
        public string Pilot { get; set; }
    }

在这里我把SpaceBody指向SpaceObjects表,当然你也可以把表命名为 "SpaceBodies" 以减少潜在的混淆,或者为最低层的基类使用一个不同的名称,并将SpaceObject用作TPH映射的子类。

然后在配置中:

            modelBuilder.Entity<SpaceBody>()
                .HasDiscriminator<string>("Discriminator")
                .HasValue<Planet>(Planet.Discriminator)
                .HasValue<Asteroid>(Asteroid.Discriminator);

            modelBuilder.Entity<Pod>();

DbContext可以包含 DbSet<SpaceBody>DbSet<Pod>,然后对实体和pods进行读写。

测试:

            using var context = new TestDbContext();

            var planets = new[] {
                new Planet { Name = "Earth" },
                new Planet{Name = "Mars"}
            };

            var asteroids = new[] {
                new Asteroid { Name = "X-31" },
                new Asteroid{Name = "XQ-101"}
            };

            var pod = new Pod { Name = "Enterprise", Pilot = "Kirk" };

            foreach(var  planet in planets)
                context.SpaceBodies.Add(planet);
            foreach(var asteroid in asteroids)
                context.SpaceBodies.Add(asteroid);
            context.Pods.Add(pod);
            context.SaveChanges();

            var testPlanets = context.SpaceBodies.OfType<Planet>().ToList();
            var testPod = context.Pods.FirstOrDefault(x => x.Pilot == "Kirk");

不过,这确实要求Pods表遵循在基类和TPH表中使用的相同列名。然而,如果你确实想使用不同的列名,例如Pods表使用 "PodName" 而不是 "Name",那么你可以在配置中引入映射变更:

            modelBuilder.Entity<Pod>()
                .Property(x => x.Name)
                .HasColumnName("PodName");
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章