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


79

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를 사용하여 어떻게 일대일 선택적 관계를 만들 수 있습니까?

답변:


101

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 / 일) 관계는 코드에서 먼저 구성해야하는 가장 혼란스러운 관계 중 하나이므로 혼자가 아닙니다! :)


1
그것은 선택할 FK 필드에 대한 사용자를 제한하지 않습니까? (나는 그가 그것을 원한다고 생각하지만 그는 회신하지 않았기 때문에 나는 모른다), 그가 수업에 FK 필드를 갖고 싶어하는 것처럼 보입니다.
Frans Bouma

1
동의합니다. EF 작동 방식의 한계입니다. 1 : 1 및 1 : 0..1은 기본 키에 따라 다릅니다. 그렇지 않으면 EF에서 여전히 지원되지 않는 "고유 한 FK"에 빠져 있다고 생각합니다. :( (? 당신은 ... 더 DB 전문가의있어하는 것은 그 올바른 ...이 고유 FK에 대해 정말입니다) 그리고 따라 곧 EF6에 될 수 없습니다 : entityframework.codeplex.com/workitem/299
줄리 러먼

1
예 @FransBouma, PIIUserId 및 LoyaltUserId 필드를 외래 키로 사용하고 싶었습니다. 그러나 EF는 당신과 Julie가 언급했듯이 이러한 상황에서 우리를 제한합니다. 응답 해 주셔서 감사합니다.
İlkay İlknur 2013.08.15

@JulieLerman 왜 사용 WithOptional합니까? 과 WithRequiredDependent와 다른 점은 무엇입니까 WithRequiredOptional?
Willy

1
WithOptional은 OP가 "PIIUser가 LoyaltyUserDetail을 가질 수 있습니다"라고 말했기 때문에 0..1 (0 또는 1)과 같은 선택적 관계를 가리 킵니다. 그리고 두 번째 질문으로 이어지는 혼란은 용어 중 하나가 잘못되어 있기 때문입니다. ;) 그것들은 WithRequiredDependent와 WithRequiredPrincipal입니다. 그게 더 말이 되나요? 종속 (일명 "자식") 또는 주일 명 "부모"인 필수 끝점을 가리 킵니다. 둘 다 동일한 일대일 관계에서도 EF는 하나를 주체로, 하나를 종속물로 참조해야합니다. HTH!
Julie Lerman

27

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

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

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


3
Julie Lerman의 답변은 질문에 답변하고 뒷받침하는 세부 사항 및 토론을 통해 올바른 방법 인 이유에 대해 자세히 설명하기 때문에 수락되었습니다 (그리고 수락 된 상태로 유지해야 함, IMHO). 대답 복사 및 붙여 넣기는 확실히 모든 곳에서 사용할 수 있으며 몇 가지를 직접 사용했지만 전문 개발자로서 빌드 할 코드를 얻는 것이 아니라 더 나은 프로그래머가되는 데 더 많은 관심을 기울여야합니다. 즉, 1 년이 지났지 만 jr가 정답을 맞췄습니다.
Mike Devenney 2017 년

1
@ClickOk 나는 이것이 오래된 질문과 코멘트라는 것을 알고 있지만 이것은 해결 방법으로 일대 다를 사용했기 때문에 정답이 아닙니다. 이것은 일대일 또는 일대 0 관계를 만들지 않습니다
Mahmoud Darwish

3

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

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 개만 생성되었음을 의미합니다.


3
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 의 문제를 해결할 것입니다.

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


예상대로 작동하지 않습니다. LoyaltyUserId를 외래 키로 사용하지 않는 마이그레이션 코드가 생성되므로 User.Id와 LoyaltyUser.Id는 동일한 값으로 끝나고 LoyaltyUserId는 비어있게됩니다.
Aaron Queenan

2

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

1
이러한 모든 유창한 API 접근 방식을 적용하기 전에 데이터 주석을 시도했습니다. 그러나 데이터 주석은 작동하지 않았습니다. 위에서 언급 한 데이터 주석을 추가하면 EF에서이 예외가 발생합니다 => 'LoyaltyUserDetail'및 'PIIUser'유형 간의 연결의 주요 끝을 확인할 수 없습니다. 이 연관의 주요 끝은 관계 유동성 API 또는 데이터 주석을 사용하여 명시 적으로 구성되어야합니다.
İlkay İlknur 2013-08-14

@ İlkayİlknur ForeignKey관계의 한쪽 끝에 만 속성을 추가하면 어떻게됩니까 ? 즉, PIIUser 또는 LoyaltyUserDetail .
pswg

Ef는 동일한 예외를 발생시킵니다.
İlkay İlknur 2013-08-14

1
@pswg 왜 PIIUser에서 FK인가? LoyaltyUserDetail에는 PIIUser가 아닌 FK가 있습니다. 따라서 LoyaltyUserDetail의 PIIUserId 속성에서 [Key, ForeignKey ( "PIIUser")] 여야합니다. 보십시오
user808128

@ user808128 탐색 속성이나 ID에 주석을 배치 할 수 있지만 차이는 없습니다.
Thomas Boby

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

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


1

이것은 원래 포스터에는 쓸모가 없지만, 외래 키가 기본 키와 달라야하는 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속성을 사용하여 세 가지 방법을 시도했지만 아무것도 작동하지 않았습니다).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.