テーブル分割Table Splitting

EF Core を使用すると、複数のエンティティを1つの行にマップできます。EF Core allows to map two or more entities to a single row. これは、_テーブル分割_または_テーブル共有_と呼ばれます。This is called table splitting or table sharing.

構成Configuration

テーブル分割を使用するには、エンティティ型を同じテーブルにマップする必要があります。主キーを同じ列にマップし、少なくとも1つのエンティティ型の主キーと同じテーブル内の別のリレーションシップを構成します。To use table splitting the entity types need to be mapped to the same table, have the primary keys mapped to the same columns and at least one relationship configured between the primary key of one entity type and another in the same table.

テーブル分割の一般的なシナリオでは、テーブル内の列のサブセットのみを使用して、パフォーマンスまたはカプセル化を向上させることができます。A common scenario for table splitting is using only a subset of the columns in the table for greater performance or encapsulation.

この例で Order は、のサブセットを表し DetailedOrder ます。In this example Order represents a subset of DetailedOrder.

public class Order
{
    public int Id { get; set; }
    public OrderStatus? Status { get; set; }
    public DetailedOrder DetailedOrder { get; set; }
}
public class DetailedOrder
{
    public int Id { get; set; }
    public OrderStatus? Status { get; set; }
    public string BillingAddress { get; set; }
    public string ShippingAddress { get; set; }
    public byte[] Version { get; set; }
}

必要な構成に加え Property(o => o.Status).HasColumnName("Status") て、を呼び出して、 DetailedOrder.Status と同じ列にマップし Order.Status ます。In addition to the required configuration we call Property(o => o.Status).HasColumnName("Status") to map DetailedOrder.Status to the same column as Order.Status.

modelBuilder.Entity<DetailedOrder>(dob =>
{
    dob.ToTable("Orders");
    dob.Property(o => o.Status).HasColumnName("Status");
});

modelBuilder.Entity<Order>(ob =>
{
    ob.ToTable("Orders");
    ob.Property(o => o.Status).HasColumnName("Status");
    ob.HasOne(o => o.DetailedOrder).WithOne()
        .HasForeignKey<DetailedOrder>(o => o.Id);
});

ヒント

詳細なコンテキストについては、完全なサンプルプロジェクトを参照してください。See the full sample project for more context.

使用法Usage

テーブル分割を使用したエンティティの保存とクエリは、他のエンティティと同じ方法で実行されます。Saving and querying entities using table splitting is done in the same way as other entities:

using (var context = new TableSplittingContext())
{
    context.Database.EnsureDeleted();
    context.Database.EnsureCreated();

    context.Add(new Order
    {
        Status = OrderStatus.Pending,
        DetailedOrder = new DetailedOrder
        {
            Status = OrderStatus.Pending,
            ShippingAddress = "221 B Baker St, London",
            BillingAddress = "11 Wall Street, New York"
        }
    });

    context.SaveChanges();
}

using (var context = new TableSplittingContext())
{
    var pendingCount = context.Orders.Count(o => o.Status == OrderStatus.Pending);
    Console.WriteLine($"Current number of pending orders: {pendingCount}");
}

using (var context = new TableSplittingContext())
{
    var order = context.DetailedOrders.First(o => o.Status == OrderStatus.Pending);
    Console.WriteLine($"First pending order will ship to: {order.ShippingAddress}");
}

省略可能な依存エンティティOptional dependent entity

注意

この機能は EF Core 3.0 で導入されました。This feature was introduced in EF Core 3.0.

依存エンティティによって使用されているすべての列が NULL データベース内に存在する場合、クエリの実行時にそのインスタンスのインスタンスは作成されません。If all of the columns used by a dependent entity are NULL in the database, then no instance for it will be created when queried. これにより、オプションの依存エンティティをモデル化できます。この場合、プリンシパルのリレーションシッププロパティは null になります。This allows modeling an optional dependent entity, where the relationship property on the principal would be null. これは、依存するすべてのプロパティが省略可能で、がに設定されている場合にも発生しますが、これは null 想定されていない可能性があります。Note that this would also happen if all of the dependent's properties are optional and set to null, which might not be expected.

コンカレンシー トークンConcurrency tokens

テーブルを共有するいずれかのエンティティ型に同時実行トークンが含まれている場合は、他のすべてのエンティティ型にも含める必要があります。If any of the entity types sharing a table has a concurrency token then it must be included in all other entity types as well. これは、同じテーブルにマップされたエンティティの1つだけが更新される場合に、古い同時実行トークンの値を回避するために必要です。This is necessary in order to avoid a stale concurrency token value when only one of the entities mapped to the same table is updated.

コンシューマー側のコードに同時実行トークンが公開されないようにするには、 shadow プロパティとして作成することができます。To avoid exposing the concurrency token to the consuming code, it's possible the create one as a shadow property:

modelBuilder.Entity<Order>()
    .Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");

modelBuilder.Entity<DetailedOrder>()
    .Property(o => o.Version).IsRowVersion().HasColumnName("Version");