web-dev-qa-db-ja.com

Entity Framework Fluent APIを使用した1対1のオプションの関係

Entity Framework Code Firstを使用して、1対1のオプションの関係を使用します。 2つのエンティティがあります。

public class PIIUser
{
    public int Id { get; set; }

    public int? LoyaltyUserDetailId { get; set; }
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    public int Id { get; set; }
    public double? AvailablePoints { get; set; }

    public int PIIUserId { get; set; }
    public PIIUser PIIUser { get; set; }
}

PIIUserにはLoyaltyUserDetailを含めることができますが、LoyaltyUserDetailにはPIIUserが必要です。これらの流approachなアプローチ手法を試しました。

modelBuilder.Entity<PIIUser>()
            .HasOptional(t => t.LoyaltyUserDetail)
            .WithOptionalPrincipal(t => t.PIIUser)
            .WillCascadeOnDelete(true);

このアプローチでは、LoyaltyUserDetailIdテーブルにPIIUsers外部キーを作成しませんでした。

その後、次のコードを試しました。

modelBuilder.Entity<LoyaltyUserDetail>()
            .HasRequired(t => t.PIIUser)
            .WithRequiredDependent(t => t.LoyaltyUserDetail);

しかし今回は、EFはこれら2つのテーブルに外部キーを作成しませんでした。

この問題に関するアイデアはありますか?エンティティフレームワークFluent APIを使用して、1対1のオプションの関係を作成するにはどうすればよいですか?

71
İlkay İlknur

EF Code Firstは、1:1および1:0..1の関係をサポートしています。後者が探しているものです(「1対0または1対1」)。

あなたの流atな試みは、ある場合には両端で必須であり、別の場合には両端でオプションと言っています。

必要なのは、一方の端にoptional、もう一方の端にrequiredです。

プログラミングE.F.コードファーストブックの例を次に示します

modelBuilder.Entity<PersonPhoto>()
.HasRequired(p => p.PhotoOf)
.WithOptional(p => p.Photo);

PersonPhotoエンティティには、PhotoOf型を指すPersonというナビゲーションプロパティがあります。 Personタイプには、Photoタイプを指すPersonPhotoというナビゲーションプロパティがあります。

2つの関連するクラスでは、各タイプの主キーを使用し、外部キー。つまり、LoyaltyUserDetailIdまたはPIIUserIdプロパティは使用しません。代わりに、関係は両方のタイプのIdフィールドに依存します。

上記のような流れるようなAPIを使用している場合、LoyaltyUser.Idを外部キーとして指定する必要はありません。EFはそれを把握します。

あなたのコードを自分でテストすることなく(私は自分の頭からこれをするのが嫌いです)...私はこれをあなたのコードに翻訳します

public class PIIUser
{
    public int Id { get; set; }    
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    public int Id { get; set; }
    public double? AvailablePoints { get; set; }    
    public PIIUser PIIUser { get; set; }
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Entity<LoyaltyUserDetail>()
  .HasRequired(lu => lu.PIIUser )
  .WithOptional(pi => pi.LoyaltyUserDetail );
}

つまり、LoyaltyUserDetails PIIUserプロパティはrequiredであり、PIIUserのLoyaltyUserDetailプロパティはオプションです。

あなたは反対側から始めることができます:

modelBuilder.Entity<PIIUser>()
.HasOptional(pi => pi.LoyaltyUserDetail)
.WithRequired(lu => lu.PIIUser);

現在、PIIUserのLoyaltyUserDetailプロパティはオプションであり、LoyaltyUserのPIIUserプロパティは必須であると言います。

常にパターンHAS/WITHを使用する必要があります。

HTHとFWIW、1対1(または1対0/1)の関係は、最初にコードで構成する最も複雑な関係の1つであるため、あなただけではありません! :)

95
Julie Lerman

LoyaltyUserDetailPIIUserの間に1対多の関係がある場合と同じように、マッピングは

modelBuilder.Entity<LoyaltyUserDetail>()
       .HasRequired(m => m.PIIUser )
       .WithMany()
       .HasForeignKey(c => c.LoyaltyUserDetailId);

EFは、必要なすべての外部キーを作成する必要があります WithManyは気にしないでください

24

コードにはいくつかの問題があります。

1:1関係は、PK <-PK(一方のPK側もFKである)、またはPK <-FK + UCのいずれかです。 、FK側は非PKであり、UCがあります。あなたのコードは、FK <-FKを持っていることを示しています。両方の側がFKを持つように定義していますが、それは間違っています。 PIIUserはPK側で、LoyaltyUserDetailはFK側です。つまり、PIIUserにはFKフィールドはありませんが、LoyaltyUserDetailにはあります。

1:1関係がオプションである場合、FK側には少なくとも1つのNULL入力可能フィールドが必要です。

p.s.w.g.上記はあなたの質問に答えましたが、PIIUserでFKも定義したという間違いを犯しました。 したがって、LoyaltyUserDetailでヌル可能FKフィールドを定義し、LoyaltyUserDetailで属性を定義してFKフィールドをマークしますが、PIIUserでFKフィールドを指定しないでください。 =

PK側(プリンシパルエンド)の側はないので、上記のp.s.w.g.のポストの下で説明した例外が発生します。

EFは一意の制約を処理できないため、1:1ではあまり得意ではありません。最初にコードの専門家ではないので、 UCを作成できるかどうかがわかりません。

(編集)btw:A 1:1 B(FK)は、Bのターゲットに作成されたFK制約が2つではなく、AのPKを指すことを意味します。

3
Frans Bouma
public class User
{
    public int Id { get; set; }
    public int? LoyaltyUserId { get; set; }
    public virtual LoyaltyUser LoyaltyUser { get; set; }
}

public class LoyaltyUser
{
    public int Id { get; set; }
    public virtual User MainUser { get; set; }
}

        modelBuilder.Entity<User>()
            .HasOptional(x => x.LoyaltyUser)
            .WithOptionalDependent(c => c.MainUser)
            .WillCascadeOnDelete(false);

これはREFERENCEおよびFOREIGN KEYSの問題を解決します

更新または削除レコード

2
pampi

ForeignKey属性をLoyaltyUserDetailプロパティに追加してみてください:

public class PIIUser
{
    ...
    public int? LoyaltyUserDetailId { get; set; }
    [ForeignKey("LoyaltyUserDetailId")]
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
    ...
}

そしてPIIUserプロパティ:

public class LoyaltyUserDetail
{
    ...
    public int PIIUserId { get; set; }
    [ForeignKey("PIIUserId")]
    public PIIUser PIIUser { get; set; }
    ...
}
2
p.s.w.g

上記のソリューションと混同される1つのことは、主キーが両方のテーブルで「Id」として定義されていることです。テーブル名に基づいた主キーがある場合、それは機能しません同じことを説明するクラス。つまり、オプションのテーブルはそれ自体の主キーを定義するのではなく、代わりにメインテーブルの同じキー名を使用する必要があります。

public class PIIUser
{
    // For illustration purpose I have named the PK as PIIUserId instead of Id
    // public int Id { get; set; }
    public int PIIUserId { get; set; }

    public int? LoyaltyUserDetailId { get; set; }
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    // Note: You cannot define a new Primary key separately as it would create one to many relationship
    // public int LoyaltyUserDetailId { get; set; }

    // Instead you would reuse the PIIUserId from the primary table, and you can mark this as Primary Key as well as foreign key to PIIUser table
    public int PIIUserId { get; set; } 
    public double? AvailablePoints { get; set; }

    public int PIIUserId { get; set; }
    public PIIUser PIIUser { get; set; }
}

そして次に

modelBuilder.Entity<PIIUser>()
.HasOptional(pi => pi.LoyaltyUserDetail)
.WithRequired(lu => lu.PIIUser);

トリックを行うだろう、受け入れられた解決策はこれを明確に説明するのに失敗し、原因を見つけるために数時間私を捨てた

0
skjagini