[c#] Entity Framework Fluent API를 사용하는 일대일 선택적 관계

Entity Framework Code First를 사용하여 일대일 선택적 관계를 사용하려고합니다. 두 개의 엔티티가 있습니다.

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. 우리는 이러한 유창한 접근 기법을 시도했습니다.

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가이 두 테이블에 외래 키를 만들지 않았습니다.

이 문제에 대한 아이디어가 있습니까? 엔티티 프레임 워크 유창한 API를 사용하여 어떻게 일대일 선택적 관계를 만들 수 있습니까?



답변

EF Code First 지원 1:11:0..1관계. 후자는 귀하가 찾고있는 것입니다 ( “일대일 또는 일대일”).

유창한 시도는 한 경우에는 양쪽 에서 필요 하고 다른 경우에는 양쪽 에서 선택 사항 이라고 말합니다 .

필요한 것은 한쪽 은 선택 사항 이고 다른 쪽은 필수 입니다.

다음은 Programming EF Code First 책의 예입니다.

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

PersonPhoto실체라는 탐색 속성이 PhotoOfA와 그 포인트 Person유형입니다. Person형태라는 탐색 속성이 Photo받는 그 점 PersonPhoto유형입니다.

두 개의 관련 클래스에서 외래 키가 아닌 각 유형의 기본 키 를 사용 합니다 . 즉, 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속성은 필수 이고 PIIUser의 LoyaltyUserDetail속성은 선택 사항입니다.

다른 쪽 끝에서 시작할 수 있습니다.

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

이제 PIIUser의 LoyaltyUserDetail속성은 선택 사항이며 LoyaltyUser의 PIIUser속성은 필수 라고 말합니다 .

항상 HAS / WITH 패턴을 사용해야합니다.

HTH 및 FWIW, 일대일 (또는 일대 0 / 일) 관계는 코드에서 먼저 구성해야하는 가장 혼란스러운 관계 중 하나이므로 혼자가 아닙니다! 🙂


답변

일대 다 관계가있는 경우 LoyaltyUserDetailPIIUser같이 매핑해야합니다.

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

EF는 필요한 모든 외래 키를 만들어야하며 WithMany는 신경 쓰지 않습니다 !


답변

코드에 몇 가지 문제가 있습니다.

1 : 1 관계는 하나이고 : PK <-pk 하나 PK 측 또한 FK이거나, 또는, PK <-fk + UC FK 측 비 PK이며 UC를 갖는다. 코드에서 FK <-FK 가 있음을 보여줍니다. 양쪽 모두 FK를 갖도록 정의했지만 그것은 잘못되었습니다. I recon PIIUser은 PK 측이고 LoyaltyUserDetailFK 측입니다. 이것은 PIIUserFK 필드가 없지만 있음을 의미 LoyaltyUserDetail합니다.

경우] 1 : 1 의 관계가 선택되면, FK 측은, 적어도 1 필드가 널한다.

위의 pswg는 귀하의 질문에 답변했지만 PIIUser에서 FK를 정의한 실수를 저질렀습니다. 물론 위에서 설명한 것처럼 잘못되었습니다. 따라서에서 nullable FK 필드를 LoyaltyUserDetail정의하고에서 속성을 정의하여 FK 필드 LoyaltyUserDetail를 표시하지만에서 FK 필드를 지정하지 마십시오 PIIUser.

PK 측이 아니기 때문에 pswg의 게시물 아래에서 위에서 설명한 예외가 발생합니다 (원칙적인 끝).

EF는 고유 한 제약 조건을 처리 할 수 ​​없기 때문에 1 : 1에서 그다지 좋지 않습니다. 저는 코드에 대한 전문가가 아니기 때문에 UC를 만들 수 있는지 여부를 모릅니다.

(편집) btw : A 1 : 1 B (FK)는 B의 타겟에서 2가 아닌 A의 PK를 가리키는 FK 제약이 1 개만 생성되었음을 의미합니다.


답변

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);

이것은 REFERENCEFOREIGN KEYS 의 문제를 해결할 것입니다.

레코드를 업데이트 하거나 삭제할


답변

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; }
    ...
}


답변

위의 솔루션과 혼동되는 한 가지는 기본 키가 두 테이블 모두에서 ” 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);

속임수를 쓰겠습니까? 수용된 솔루션은 이것을 명확하게 설명하지 못하고 원인을 찾기 위해 몇 시간 동안 저를 버렸습니다.


답변

이것은 원래 포스터에는 쓸모가 없지만, 외래 키가 기본 키와 달라야하는 EF6 사용자를 위해 다음 방법을 사용합니다.

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; }
}

modelBuilder.Entity<PIIUser>()
    .HasRequired(t => t.LoyaltyUserDetail)
    .WithOptional(t => t.PIIUser)
    .Map(m => m.MapKey("LoyaltyUserDetailId"));

LoyaltyUserDetailId내가 말할 수있는 한 유창한 API를 사용해서 만 지정할 수 있기 때문에 필드를 사용할 수 없습니다 . ( ForeignKey속성을 사용하여 세 가지 방법을 시도했지만 아무것도 작동하지 않았습니다).