Entity Framework
Entity Frameworkとのマッピング関係コード1番目:1対多および多対多
サーチ…
前書き
このトピックでは、Entity Frameworkコードファーストを使用して、1対多の関係と多対多の関係をマッピングする方法について説明します。
1対多のマッピング
つまり、次のような2つの異なるエンティティがあるとしましょう。
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
}
public class Car
{
public int CarId { get; set; }
public string LicensePlate { get; set; }
}
public class MyDemoContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Car> Cars { get; set; }
}
そして、それらの間に1対多の関係を設定したい、つまり、1人が0台、1台以上の車を持つことができ、1台の車が1人の人に正確に属します。すべての関係は双方向であるため、人が車を持っていると、車はその人に属します。
これを行うには、モデルクラスを変更するだけです:
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public virtual ICollection<Car> Cars { get; set; } // don't forget to initialize (use HashSet)
}
public class Car
{
public int CarId { get; set; }
public string LicensePlate { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
それはそれです:)あなたはすでにあなたの関係をセットアップしています。データベースでは、これはもちろん外部キーで表されます。
一対多のマッピング:大会に反対
最後の例では、EFがどの列が外部キーであるのか、どこを指すのかを把握しています。どうやって?慣例を使用する。型のプロパティ持つPerson
という名前のPerson
してPersonId
プロパティは、という結論にEFをリードPersonId
外部キーであり、それはタイプによって表されるテーブルの主キーを指しPerson
。
しかし、 車タイプでPersonIdをOwnerIdとPerson to Ownerに変更するとどうなりますか?
public class Car { public int CarId { get; set; } public string LicensePlate { get; set; } public int OwnerId { get; set; } public virtual Person Owner { get; set; } }
残念ながら、この場合、規則は正しいDBスキーマを生成するには不十分です。
心配ない;モデル内の関係とキーに関するヒントをEFに役立てることができます。 OwnerId
プロパティをFKとして使用するように、 Car
タイプを設定するだけです。エンティティタイプのコンフィグレーションを作成し、 OnModelCreating()
で適用します:
public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
public CarEntityTypeConfiguration()
{
this.HasRequired(c => c.Owner).WithMany(p => p.Cars).HasForeignKey(c => c.OwnerId);
}
}
これは、基本的に、 Car
には必須のプロパティOwner
( HasRequired() )があり、 Owner
のタイプでは、 Cars
プロパティは車のエンティティ( WithMany() )を参照するために使用されます。最後に、外部キーを表すプロパティが指定されます( HasForeignKey() )。これは私たちが望むスキーマを提供します:
Person
側からも関係を設定することができます:
public class PersonEntityTypeConfiguration : EntityTypeConfiguration<Person>
{
public PersonEntityTypeConfiguration()
{
this.HasMany(p => p.Cars).WithRequired(c => c.Owner).HasForeignKey(c => c.OwnerId);
}
}
アイデアは同じですが、両サイドが異なります(「この人は多くの車を所有しており、それぞれの車には必要な所有者がいます」)。 Person
側またはCar
側からリレーションシップをコンフィグレーションする場合は関係ありません。両方を含めることもできますが、この場合は両側に同じ関係を指定するように注意してください!
0または1対多のマッピング
前の例では、人がいなくても車は存在できません。その人を車の側からのオプションにしたい場合はどうすればいいですか?まあ、1対多のやり方を知っているのは簡単です。 Car
のPersonId
をnullに変更するだけです:
public class Car
{
public int CarId { get; set; }
public string LicensePlate { get; set; }
public int? PersonId { get; set; }
public virtual Person Person { get; set; }
}
そして、設定を行う側からHasOptional() (またはWithOptional()を使用します):
public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
public CarEntityTypeConfiguration()
{
this.HasOptional(c => c.Owner).WithMany(p => p.Cars).HasForeignKey(c => c.OwnerId);
}
}
多対多
すべての人が複数の車を持ち、すべての車が複数の所有者を持つことができる他のシナリオに移りましょう(ただし、関係は双方向です)。これは多対多の関係です。最も簡単な方法は、EFに慣習を使って魔法をさせることです。
次のようにモデルを変更してください:
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public virtual ICollection<Car> Cars { get; set; }
}
public class Car
{
public int CarId { get; set; }
public string LicensePlate { get; set; }
public virtual ICollection<Person> Owners { get; set; }
}
スキーマ:
ほぼ完璧です。ご覧のように、EFは人と車のペアリングを記録できる結合テーブルの必要性を認識していました。
多対多:結合表のカスタマイズ
ジョインテーブルのフィールドの名前をもう少し親切にすることができます。これは、通常の設定方法を使用して行うことができます(設定をどちらの側から行うかは関係ありません)。
public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
public CarEntityTypeConfiguration()
{
this.HasMany(c => c.Owners).WithMany(p => p.Cars)
.Map(m =>
{
m.MapLeftKey("OwnerId");
m.MapRightKey("CarId");
m.ToTable("PersonCars");
}
);
}
}
でも読みするのはとても簡単:この車は多くの所有者(持ちhasManyの() )、それぞれの所有者は多くの車を有する( WithManyを() )。あなたがOwnerIdにするには、左側のキーをマップするように、この地図( MapLeftKey() )、CarIdに右キー( MapRightKey()テーブルPersonCars)と全部( ToTable() )。これにより、正確にそのスキーマが得られます。
多対多:カスタム結合エンティティ
私は、EFが結合エンティティを使用して結合テーブルを推論できるようにするファンではないことを認めなければなりません。あなたはテーブルを修正することができないので、人と自動車との関連付けに余分な情報を追跡することはできません(それが有効な日付を言いましょう)。
また、結合表のCarId
は主キーの一部です。そのため、家族が新しい車を購入する場合は、古い関連を削除して新しいものを追加する必要があります。 EFはあなたからこれを隠しますが、これはあなたの代わりに、単純な更新(頻繁挿入/削除がインデックスの断片化につながる可能性があることは言うまでもありません-良いこと、これらの2つの操作を行う必要があることを意味簡単な修正があり 、そのためには)。
この場合、1つの特定の自動車と特定の1人の個人の両方への参照を持つ結合エンティティを作成することができます。基本的には、多対多の関連付けを2つの1対多関連の組み合わせとして見ます:
public class PersonToCar
{
public int PersonToCarId { get; set; }
public int CarId { get; set; }
public virtual Car Car { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
public DateTime ValidFrom { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public virtual ICollection<PersonToCar> CarOwnerShips { get; set; }
}
public class Car
{
public int CarId { get; set; }
public string LicensePlate { get; set; }
public virtual ICollection<PersonToCar> Ownerships { get; set; }
}
public class MyDemoContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Car> Cars { get; set; }
public DbSet<PersonToCar> PersonToCars { get; set; }
}
これにより、より多くの制御が可能になり、より柔軟になります。今ではカスタムデータをアソシエーションに追加することができ、すべてのアソシエーションに独自のプライマリキーがあるため、その中の車やオーナーの参照を更新できます。
これは実際には2対1の関係の組み合わせに過ぎないので、前の例で説明したすべての設定オプションを使用できます。