EF Core - 从每个类型 Table 迁移到每个层次结构 Table
EF Core - Migrate from Table Per Type to Table Per Hierarchy
我正在尝试从使用 TPT(每个子类一个 table)迁移到 TPH(所有子类一个 table)。
这是我的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>
{
}
创建模型时:
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
的 table。究竟是什么导致了 2 tables - GroceryItems
和 MenuItems
?因为我尝试了一些东西,但它们没有用。 EF Core 默认使用 TPH,所以我不确定它为什么使用 TPT。我想知道是不是因为我的基础实体是通用类型。
EF Core uses TPH by default so I'm unsure why it is using TPT. I'm wondering if it is because my base entity is a generic type.
通用类型是问题之一。另一个是它不是一个基础实体,而只是基础class。为了被视为实体,必须有 DbSet<T>
或 ModelBuilder.Entity<T>()
调用,或应用 IEntityTypeConfiguration<T>
,或某些发现的实体导航 属性(集合或引用)引用到它 - 见 Including types in the model.
你没有任何这些,所以模型甚至不是 TPT(包含共同属性的共同 table + 每个包含特定属性的派生实体的单个 table),而是某种TPC 的(Table-Per-Class,目前不受 EF Core 支持),其中没有通用的 table - 所有数据都在具体的 table 中每个派生实体。
因此,为了使用 TPT,您需要解决这两个问题。泛型class不能用作实体类型,因为它的类型不足以识别它(每个泛型实例化都是不同的类型,typeof(Foo<Bar>) != typeof(Foo<Baz>)
)。
首先提取将用作基本实体的非通用部分(为清楚起见,删除了非 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 =>
{
});
我正在尝试从使用 TPT(每个子类一个 table)迁移到 TPH(所有子类一个 table)。
这是我的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>
{
}
创建模型时:
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
的 table。究竟是什么导致了 2 tables - GroceryItems
和 MenuItems
?因为我尝试了一些东西,但它们没有用。 EF Core 默认使用 TPH,所以我不确定它为什么使用 TPT。我想知道是不是因为我的基础实体是通用类型。
EF Core uses TPH by default so I'm unsure why it is using TPT. I'm wondering if it is because my base entity is a generic type.
通用类型是问题之一。另一个是它不是一个基础实体,而只是基础class。为了被视为实体,必须有 DbSet<T>
或 ModelBuilder.Entity<T>()
调用,或应用 IEntityTypeConfiguration<T>
,或某些发现的实体导航 属性(集合或引用)引用到它 - 见 Including types in the model.
你没有任何这些,所以模型甚至不是 TPT(包含共同属性的共同 table + 每个包含特定属性的派生实体的单个 table),而是某种TPC 的(Table-Per-Class,目前不受 EF Core 支持),其中没有通用的 table - 所有数据都在具体的 table 中每个派生实体。
因此,为了使用 TPT,您需要解决这两个问题。泛型class不能用作实体类型,因为它的类型不足以识别它(每个泛型实例化都是不同的类型,typeof(Foo<Bar>) != typeof(Foo<Baz>)
)。
首先提取将用作基本实体的非通用部分(为清楚起见,删除了非 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 =>
{
});