表拆分Table Splitting

EF Core 允许将两个或多个实体映射到单个行。 这称为 “表拆分“ 或 “表共享“。

配置Configuration

若要使用表拆分,则需要将多个实体类型映射到同一个表中,将主键映射到相同的列,并且在同一个实体类型的主键与另一个实体类型的主键之间配置至少一个关系。

表拆分的常见方案是只使用表中的部分列,以获得更好的性能或封装。

在此示例中 Order 表示 DetailedOrder的子集。

  1. public class Order
  2. {
  3. public int Id { get; set; }
  4. public OrderStatus? Status { get; set; }
  5. public DetailedOrder DetailedOrder { get; set; }
  6. }
  1. public class DetailedOrder
  2. {
  3. public int Id { get; set; }
  4. public OrderStatus? Status { get; set; }
  5. public string BillingAddress { get; set; }
  6. public string ShippingAddress { get; set; }
  7. public byte[] Version { get; set; }
  8. }

除了所需的配置之外,我们还 Property(o => o.Status).HasColumnName("Status")DetailedOrder.Status 映射到与 Order.Status相同的列。

  1. modelBuilder.Entity<DetailedOrder>(dob =>
  2. {
  3. dob.ToTable("Orders");
  4. dob.Property(o => o.Status).HasColumnName("Status");
  5. });
  6. modelBuilder.Entity<Order>(ob =>
  7. {
  8. ob.ToTable("Orders");
  9. ob.Property(o => o.Status).HasColumnName("Status");
  10. ob.HasOne(o => o.DetailedOrder).WithOne()
  11. .HasForeignKey<DetailedOrder>(o => o.Id);
  12. });

提示

有关更多上下文,请参阅完整的示例项目

用法Usage

使用表拆分来保存和查询实体的方式与其他实体相同:

  1. using (var context = new TableSplittingContext())
  2. {
  3. context.Database.EnsureDeleted();
  4. context.Database.EnsureCreated();
  5. context.Add(new Order
  6. {
  7. Status = OrderStatus.Pending,
  8. DetailedOrder = new DetailedOrder
  9. {
  10. Status = OrderStatus.Pending,
  11. ShippingAddress = "221 B Baker St, London",
  12. BillingAddress = "11 Wall Street, New York"
  13. }
  14. });
  15. context.SaveChanges();
  16. }
  17. using (var context = new TableSplittingContext())
  18. {
  19. var pendingCount = context.Orders.Count(o => o.Status == OrderStatus.Pending);
  20. Console.WriteLine($"Current number of pending orders: {pendingCount}");
  21. }
  22. using (var context = new TableSplittingContext())
  23. {
  24. var order = context.DetailedOrders.First(o => o.Status == OrderStatus.Pending);
  25. Console.WriteLine($"First pending order will ship to: {order.ShippingAddress}");
  26. }

可选依赖实体Optional dependent entity

备注

EF Core 3.0 中引入了此功能。

如果依赖实体使用的所有列都 NULL 在数据库中,则查询时将不会创建该实体的任何实例。 这允许对一个可选的依赖实体建模,其中主体的关系属性将为 null。 请注意,这也会导致所有依赖属性均为可选,并设置为 null,这可能不是预期的。

并发令牌Concurrency tokens

如果共享表的任何实体类型都有并发标记,则还必须将其包含在所有其他实体类型中。 当只更新映射到同一个表中的一个实体时,必须使用此值来避免陈旧并发令牌值。

若要避免向使用代码公开并发标记,可以将其创建为影子属性

  1. modelBuilder.Entity<Order>()
  2. .Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
  3. modelBuilder.Entity<DetailedOrder>()
  4. .Property(o => o.Version).IsRowVersion().HasColumnName("Version");