次のサンプル データベース スキーマ (SQL Server) の Entity Framework Code-First マッピングを作成する際に問題が発生しています。

すべてのテーブルには、TenantIdすべての (複合) プライマリ キーと外部キー (マルチテナンシー) の一部である が含まれています。
ACompanyは aCustomerまたは a のいずれかであり、 SupplierTable-Per-Type (TPT) 継承マッピングを介してこれをモデル化しようとしています。
public abstract class Company
{
public int TenantId { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public Address Address { get; set; }
}
public class Customer : Company
{
public string CustomerName { get; set; }
public int SalesPersonId { get; set; }
public Person SalesPerson { get; set; }
}
public class Supplier : Company
{
public string SupplierName { get; set; }
}
Fluent API を使用したマッピング:
modelBuilder.Entity<Company>()
.HasKey(c => new { c.TenantId, c.CompanyId });
modelBuilder.Entity<Customer>()
.ToTable("Customers");
modelBuilder.Entity<Supplier>()
.ToTable("Suppliers");
ベース テーブルCompaniesには と 1 対多の関係がありAddress(顧客かサプライヤかに関係なく、すべての会社に住所があります)、この関連付けのマッピングを作成できます。
modelBuilder.Entity<Company>()
.HasRequired(c => c.Address)
.WithMany()
.HasForeignKey(c => new { c.TenantId, c.AddressId });
外部キーは、主キーの一部であるTenantIdと別の列である で構成されますAddressId。これは機能します。
データベース スキーマでわかるように、データベースの観点からすると、Customerとの関係Personは基本的に と の 1 対多の関係と同じですCompany。Address外部キーはTenantId(主キーの一部) と列で構成されます。 SalesPersonId. (顧客だけが営業担当者ではなく営業担当者を持っているSupplierため、関係は今回は基本クラスではなく派生クラスにあります。)
前と同じ方法で、Fluent API とのこの関係のマッピングを作成しようとします。
modelBuilder.Entity<Customer>()
.HasRequired(c => c.SalesPerson)
.WithMany()
.HasForeignKey(c => new { c.TenantId, c.SalesPersonId });
しかし、EF がモデルをコンパイルしようとすると、次のエラーInvalidOperationExceptionがスローされます。
外部キー コンポーネント 'TenantId' は、タイプ 'Customer' で宣言されたプロパティではありません。モデルから明示的に除外されていないこと、および有効なプリミティブ プロパティであることを確認してください。
どうやら、基本クラスのプロパティと派生クラスの別のプロパティから外部キーを作成することはできません (ただし、データベース スキーマでは、外部キーは派生型の table の両方Customerの列で構成されます)。
おそらく動作させるために2つの変更を試みました:
Customerとの間の外部キーの関連付けをPerson独立した関連付けに変更しました。つまり、プロパティSalesPersonIdを削除してから、マッピングを試みました。modelBuilder.Entity<Customer>() .HasRequired(c => c.SalesPerson) .WithMany() .Map(m => m.MapKey("TenantId", "SalesPersonId"));それは役に立ちません(私は本当に望んでいませんでした)そして例外は次のとおりです:
指定されたスキーマは無効です。... タイプ内の各プロパティ名は一意である必要があります。プロパティ名 'TenantId' は既に定義されています。
TPT から TPH へのマッピングが変更されました。つまり、2 つの
ToTable呼び出しが削除されました。しかし、それは同じ例外をスローします。
2 つの回避策があります。
クラスに a
SalesPersonTenantIdを導入します。Customerpublic class Customer : Company { public string CustomerName { get; set; } public int SalesPersonTenantId { get; set; } public int SalesPersonId { get; set; } public Person SalesPerson { get; set; } }およびマッピング:
modelBuilder.Entity<Customer>() .HasRequired(c => c.SalesPerson) .WithMany() .HasForeignKey(c => new { c.SalesPersonTenantId, c.SalesPersonId });これをテストしましたが、動作します。
SalesPersonTenantIdただし、に加えて、Customersテーブルに新しい列がありますTenantId。ビジネスの観点から、両方の列が常に同じ値である必要があるため、この列は冗長です。Company継承マッピングを放棄し、 と の間、Customerと の間Companyに1 対 1 のマッピングを作成しますSupplier。Company次に、抽象型ではなく具象型になる必要があり、 に 2 つのナビゲーション プロパティがありCompanyます。しかし、このモデルは、企業が顧客またはサプライヤーのいずれかであり、同時に両方になることはできないことを正しく表現していません。私はそれをテストしませんでしたが、うまくいくと信じています。
テストした完全な例 (コンソール アプリケーション、NuGet 経由でダウンロードした EF 4.3.1 アセンブリへの参照) をここに貼り付けます。
using System;
using System.Data.Entity;
namespace EFTPTCompositeKeys
{
public abstract class Company
{
public int TenantId { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public Address Address { get; set; }
}
public class Customer : Company
{
public string CustomerName { get; set; }
public int SalesPersonId { get; set; }
public Person SalesPerson { get; set; }
}
public class Supplier : Company
{
public string SupplierName { get; set; }
}
public class Address
{
public int TenantId { get; set; }
public int AddressId { get; set; }
public string City { get; set; }
}
public class Person
{
public int TenantId { get; set; }
public int PersonId { get; set; }
public string Name { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Company> Companies { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>()
.HasKey(c => new { c.TenantId, c.CompanyId });
modelBuilder.Entity<Company>()
.HasRequired(c => c.Address)
.WithMany()
.HasForeignKey(c => new { c.TenantId, c.AddressId });
modelBuilder.Entity<Customer>()
.ToTable("Customers");
// the following mapping doesn't work and causes an exception
modelBuilder.Entity<Customer>()
.HasRequired(c => c.SalesPerson)
.WithMany()
.HasForeignKey(c => new { c.TenantId, c.SalesPersonId });
modelBuilder.Entity<Supplier>()
.ToTable("Suppliers");
modelBuilder.Entity<Address>()
.HasKey(a => new { a.TenantId, a.AddressId });
modelBuilder.Entity<Person>()
.HasKey(p => new { p.TenantId, p.PersonId });
}
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
using (var ctx = new MyContext())
{
try
{
ctx.Database.Initialize(true);
}
catch (Exception e)
{
throw;
}
}
}
}
}
質問: 上記のデータベース スキーマを Entity Framework を使用してクラス モデルにマップする方法はありますか?