【问题标题】:EF Core - Migrate from Table Per Type to Table Per HierarchyEF Core - 从每个类型的表迁移到每个层次结构的表
【发布时间】:2021-08-10 05:33:22
【问题描述】:

我正在尝试从使用 TPT(每个子类一个表)迁移到 TPH(一个表用于所有子类)。

这是我 TPT 的起点:

实体:

[Serializable]
public abstract class VeganItem<TVeganItemEstablishment> : DomainEntity<int>
{
    [Required]
    public string Name { get; set; }
    [Required]
    public string CompanyName { get; set; }
    [Required]
    public string Description { get; set; }
    public string Image { get; set; }
    [Required]
    public int IsNotVeganCount { get; set; } = 0;
    [Required]
    public int IsVeganCount { get; set; } = 0;
    [Required]
    public int RatingsCount { get; set; } = 0;
    [Required]
    public int Rating { get; set; }
    [Required]
    public List<Option> Tags { get; set; }

    [PropertyName("veganItemEstablishments", Ignore = true)]
    public virtual ICollection<TVeganItemEstablishment> VeganItemEstablishments { get; set; }
}


[ElasticsearchType(RelationName = "groceryitem", IdProperty = "Id")]
public class GroceryItem : VeganItem<GroceryItemEstablishment>
{
}

[ElasticsearchType(RelationName = "menuitem", IdProperty = "Id")]
public class MenuItem : VeganItem<MenuItemEstablishment>
{
}

OnModelCreating:

modelBuilder.Entity<GroceryItem>(gi =>
{
    gi.HasIndex(e => new { e.CompanyName, e.Name }).IsUnique();
    gi.Property(u => u.CreatedDate)
        .HasDefaultValueSql("CURRENT_TIMESTAMP"); 
    gi.Property(u => u.UpdatedDate)
        .HasDefaultValueSql("CURRENT_TIMESTAMP"); 
    gi.HasKey(e => e.Id);
    gi.HasOne(q => q.UpdatedBy)
        .WithMany()
        .HasForeignKey(k => k.UpdatedById);
    gi.HasOne(q => q.CreatedBy)
        .WithMany()
        .HasForeignKey(k => k.CreatedById);
    gi.Property(e => e.Tags)
        .HasConversion(
            v => JsonSerializer.Serialize(v, null),
            v => JsonSerializer.Deserialize<List<Option>>(v, null),
            new ValueComparer<IList<Option>>(
                (c1, c2) => c1.SequenceEqual(c2),
                c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
                c => (IList<Option>)c.ToList()));
});

modelBuilder.Entity<MenuItem>(mi =>
{
    mi.HasIndex(e => new { e.CompanyName, e.Name }).IsUnique();
    mi.Property(u => u.CreatedDate)
        .HasDefaultValueSql("CURRENT_TIMESTAMP"); 
    mi.Property(u => u.UpdatedDate)
        .HasDefaultValueSql("CURRENT_TIMESTAMP"); 
    mi.HasKey(e => e.Id);
    mi.HasOne(q => q.UpdatedBy)
        .WithMany()
        .HasForeignKey(k => k.UpdatedById);
    mi.HasOne(q => q.CreatedBy)
        .WithMany()
        .HasForeignKey(k => k.CreatedById);
    mi.Property(e => e.Tags)
    .HasConversion(
        v => JsonSerializer.Serialize(v, null),
        v => JsonSerializer.Deserialize<List<Option>>(v, null),
        new ValueComparer<IList<Option>>(
            (c1, c2) => c1.SequenceEqual(c2),
            c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
            c => (IList<Option>)c.ToList()));
});

public DbSet<GroceryItem> GroceryItems { get; set; }
public DbSet<MenuItem> MenuItems { get; set; }

所以我只想要一张名为VeganItems 的表。实际上是什么导致有 2 个表 - GroceryItemsMenuItems?因为我尝试了几件事,但它们没有用。 EF Core 默认使用 TPH,所以我不确定它为什么使用 TPT。我想知道是不是因为我的基本实体是泛型类型。

【问题讨论】:

    标签: asp.net-core entity-framework-core ef-core-5.0


    【解决方案1】:

    EF Core 默认使用 TPH,所以我不确定它为什么使用 TPT。我想知道是不是因为我的基本实体是泛型类型。

    泛型类型是问题之一。另一个是它不是基础entity,而只是基础class。为了被视为实体,必须有DbSet&lt;T&gt;ModelBuilder.Entity&lt;T&gt;() 调用,或应用IEntityTypeConfiguration&lt;T&gt;,或引用它的一些不相关的实体导航属性(集合或引用) - 请参阅Including types in the model

    您没有任何这些,因此模型甚至不是 TPT(包含公共属性的公共表 + 每个包含特定属性的派生实体的单个表),而是某种 TPC(每个类的表,当前不受 EF Core 支持),其中没有公用表 - 所有数据都在每个派生实体的具体表中。

    因此,为了使用 TPT,您需要解决这两个问题。泛型类不能用作实体类型,因为它的类型不足以识别它(每个泛型实例都是不同的类型,typeof(Foo&lt;Bar&gt;) != typeof(Foo&lt;Baz&gt;))。

    首先提取将用作基本实体的非通用部分(为清楚起见,删除了非 EF Core 注释):

    // Base class (code/data reuse only, not an entity)
    public abstract class DomainEntity<TId>
    {
        public TId Id { get; set; }
    }
    
    // Base entity
    public abstract class VeganItem : DomainEntity<int>
    {
        [Required]
        public string Name { get; set; }
        [Required]
        public string CompanyName { get; set; }
        [Required]
        public string Description { get; set; }
        public string Image { get; set; }
        [Required]
        public int IsNotVeganCount { get; set; } = 0;
        [Required]
        public int IsVeganCount { get; set; } = 0;
        [Required]
        public int RatingsCount { get; set; } = 0;
        [Required]
        public int Rating { get; set; }
        [Required]
        public List<Option> Tags { get; set; }
    
    }
    
    // Base class (code/data reuse only, not an entity)
    public abstract class VeganItem<TVeganItemEstablishment> : VeganItem
    {
        public virtual ICollection<TVeganItemEstablishment> VeganItemEstablishments { get; set; }
    }
    
    // Derived entity
    public class GroceryItem : VeganItem<GroceryItemEstablishment>
    {
    }
    
    // Derived entity
    public class MenuItem : VeganItem<MenuItemEstablishment>
    {
    }
    

    然后(可选)为其添加DbSet

    public DbSet<VeganItem> VeganItems { get; set; }
    
    

    最后(强制)将基实体成员的流式配置移动到自己的块中,并且只保留派生类型的特定成员的配置:

    
    // Configure base entity
    modelBuilder.Entity<VeganItem>(vi =>
    {
        vi.HasIndex(e => new { e.CompanyName, e.Name }).IsUnique();
        vi.Property(u => u.CreatedDate)
            .HasDefaultValueSql("CURRENT_TIMESTAMP");
        vi.Property(u => u.UpdatedDate)
            .HasDefaultValueSql("CURRENT_TIMESTAMP");
        vi.HasKey(e => e.Id);
        vi.HasOne(q => q.UpdatedBy)
            .WithMany()
            .HasForeignKey(k => k.UpdatedById);
        vi.HasOne(q => q.CreatedBy)
            .WithMany()
            .HasForeignKey(k => k.CreatedById);
        vi.Property(e => e.Tags)
            .HasConversion(
                v => JsonSerializer.Serialize(v, null),
                v => JsonSerializer.Deserialize<List<Option>>(v, null),
                new ValueComparer<IList<Option>>(
                    (c1, c2) => c1.SequenceEqual(c2),
                    c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
                    c => (IList<Option>)c.ToList()));
    });
    
    // Configure derived entities
    modelBuilder.Entity<GroceryItem>(gi =>
    {
    });
    
    modelBuilder.Entity<MenuItem>(mi =>
    {
    });
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-12
      相关资源
      最近更新 更多