私はこれを理解しようとして、週末の大部分を費やしました。
TPH 継承戦略を使用してドメイン内のエンティティにマップするサブスクリプション テーブルを含む既存のデータベースがあります。
サブスクリプションにはいくつかの種類があり、それぞれに独自の特性があるため、継承が必要です。これは、ドメインの単純化されたバージョンです。
public class Subscription
{
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
public int SubscriptionID { get; set; }
public string Name { get; set; }
}
public class BookSubscription:Subscription
{
public string Book { get; set; }
}
public class TVSubscription : Subscription
{
public string Channel { get; set; }
}
これらのエンティティは、コンテキストで次のようにマップされます。
public DbSet<Customer> Customers { get; set; }
public DbSet<Subscription> Subscriptions { get; set; }
public DbSet<TVSubscription> TVSubscriptions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().ToTable("tblCustomer");
modelBuilder.Entity<Customer>().HasKey(t => t.CustomerID);
modelBuilder.Entity<Subscription>()
.Map<Subscription>(m =>
{
m.ToTable("tblSubscription");
m.Requires("ServiceTypeId").HasValue(1);
})
.Map<TVSubscription>(m =>
{
m.Requires("ServiceTypeId").HasValue(20);
})
.Map<BookSubscription>(m =>
{
m.Requires("ServiceTypeId").HasValue(5);
}
);
modelBuilder.Entity<Subscription>().HasKey(s => s.SubscriptionID);
base.OnModelCreating(modelBuilder);
}
予想どおり、いずれかの派生クラスに対してコンテキストがクエリされると、EF は ServiceTypeId を識別子として使用してクエリを正しく構築します。Customer エンティティに派生クラスを含めようとすると、問題が発生します。これは機能します:
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
public virtual ICollection<Subscription> Subscriptions{ get; set; }
}
これは動作しません:
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
public virtual ICollection<Subscription> Subscriptions{ get; set; }
public virtual ICollection<TVSubscription> TvSubscriptions { get; set; }
}
これもありません:
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
public virtual ICollection<TVSubscription> TvSubscriptions { get; set; }
}
プロファイラーを確認すると、これらのシナリオの両方で、EF が _id サフィックスを追加し、customerID に加えて存在しない customer_id 列を照会しようとしていることがわかります。派生型の外部キーを明示的にマップしようとしましたが、EF は次のように不平を言っていました。
外部キー コンポーネント 'CustomerID' は、タイプ 'TVSubscription' で宣言されたプロパティではありません。モデルから明示的に除外されていないこと、および有効なプリミティブ プロパティであることを確認してください。
CustomerID は派生クラスにはありませんが、そこに移動しようとしても機能しませんでした (EF が別の継承戦略でマップしようとしたためと思われます)。
今、私はこのようなもので行くことができることを理解しています:
public IEnumerable<TVSubscription> TvSubscriptions
{
get { return Subscriptions.OfType<TVSubscription>(); }
}
ただし、このアプローチでは、EF は db に対してサブスクリプションのセット全体をクエリし、ローカルでフィルター処理して TVSubscriptions のみを返します。Customer は何百ものサブスクリプションを持つことができるため、これは確かに理想的ではありません。できれば、dbContext を使用する場合と同様に、特定の種類のサブスクリプションのみを EF にクエリして返すようにしたいと考えています。
問題は、どうすれば TVSubscriptions のリストを Customer クラスに含めて、それらのみに対して EF クエリを実行できるかということです。
これが不可能な場合は、派生クラスのコレクションを Customer エンティティに追加するときに EF がどのような仮定を行っているかを誰かが説明できますか?