web-dev-qa-db-ja.com

Entity Framework 6のHasOptional()。WithOptionalDependent()リレーションでの外部キーのマッピング

Entity Framework 6.1.3には次のデータモデルがあります。

using System.Data.Entity;

public class Student
{
    public int Id { get; set; }
    public virtual Contact Contact { get; set; }
}

public class Contact
{
    public int Id { get; set; }
    public virtual Student Student { get; set; }
}

public class MyContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder builder)
    {
        builder.Entity<Contact>()
            .HasOptional(x => x.Student)
            .WithOptionalDependent(x => x.Contact)
            .WillCascadeOnDelete(true);
    }
}

public static class Program
{
    private static void Main()
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());

        using (var context = new MyContext())
            context.Database.Initialize(force: true);
    }
}

このコードを起動すると、目的とする正確なテーブル構造が得られます。

dbo.Contacts
    Id (PK)
    Student_Id (FK, NULL, CASCADE ON DELETE)

dbo.Students
    Id (PK)

ただし、ここでContactエンティティで使用できるようにStudent_Idプロパティを追加します。そのため、Student_Idナビゲーションを介して他のテーブルを結合する必要なく、.Student.Idを読み取ることができます。

プロパティをContactエンティティに追加すると、2つの列Student_IdStudent_Id1が表示されるか、Each property name in a type must be unique.というエラーメッセージが表示されます。

列はすでにデータベースにありますが、必要なのはエンティティに含めることだけですが、なぜそんなに面倒なのですか?解決策はありますか?

14
Tom Pažourek

GitHubで質問 の後に、Entity Framework Program Managerから応答を得ることができました。

残念ながら、これはEF6の制限です。主キープロパティでもない限り、1対1の関係で外部キープロパティを持つことはできません。これは、EF6が代替キー/一意のインデックスをサポートしていないため、非主キープロパティが一意であることを強制できないためです。外部キーのプロパティがエンティティにないときにそれを実行できるという事実は、ちょっとした癖です...しかし、明らかに私たちが削除するものではありません????。

BTW代替キー(およびこのシナリオ)は、EF Coreでサポートされています。

– Rowan Miller @ https://github.com/aspnet/EntityFramework6/issues/159#issuecomment-274889438

13
Tom Pažourek

依存エンティティのFKプロパティを1対1の関係で宣言する場合は、PKとしても使用する必要があると思います。 EFコードが最初 必須 依存エンティティのPKも関係のFKでなければならない:

public class Contact
{
    [Key,ForeignKey("Student")]
    public int StudentId { get; set; }
    public virtual Student Student { get; set; }
}

しかし、これはあなたが探しているものではないと思います。したがって、ここには3つのオプションがあると思います。

  • 現在の関係構成を保持します。
  • 本格的な 1対1の関係 を作成します。
  • 1対多の関係を作成する

私の経験では、最後のものはあなたが達成しようとしていることに最も適応しています(しかしそれは私の意見です)。この場合、必要に応じてFkプロパティを操作できます。コレクションによってContactStudentナビゲーションプロパティを変更する必要があります(またはこのナビゲーションプロパティを省略して、単方向の関係):

public class Student
{
    public int Id { get; set; }
    public virtual ICollection<Contact> Contacts { get; set; }
}

構成は次のようになります。

 builder.Entity<Contact>()
        .HasOptional(x => x.Student)
        .WithMany(x => x.Contacts)
        .HasForeignKey(x => x.StudentId)
        .WillCascadeOnDelete(true);

更新

4番目のオプションは、2つの単方向の関係を作成することです。

 builder.Entity<Contact>()
        .HasOptional(x => x.Student)
        .WithMany()
        .HasForeignKey(x => x.StudentId)
        .WillCascadeOnDelete(true);

 builder.Entity<Student>()
        .HasOptional(x => x.Contact)
        .WithMany()
        .HasForeignKey(x => x.ContactId)
        .WillCascadeOnDelete(true);

しかし、このオプションは2つのテーブル間の実際の関係を壊します。

8
octavioccl