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:1
및 1:0..1
관계. 후자는 귀하가 찾고있는 것입니다 ( “일대일 또는 일대일”).
유창한 시도는 한 경우에는 양쪽 에서 필요 하고 다른 경우에는 양쪽 에서 선택 사항 이라고 말합니다 .
필요한 것은 한쪽 은 선택 사항 이고 다른 쪽은 필수 입니다.
다음은 Programming EF Code First 책의 예입니다.
modelBuilder.Entity<PersonPhoto>()
.HasRequired(p => p.PhotoOf)
.WithOptional(p => p.Photo);
PersonPhoto
실체라는 탐색 속성이 PhotoOf
A와 그 포인트 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 / 일) 관계는 코드에서 먼저 구성해야하는 가장 혼란스러운 관계 중 하나이므로 혼자가 아닙니다! 🙂
답변
일대 다 관계가있는 경우 LoyaltyUserDetail
와 PIIUser
같이 매핑해야합니다.
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 측이고 LoyaltyUserDetail
FK 측입니다. 이것은 PIIUser
FK 필드가 없지만 있음을 의미 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);
이것은 REFERENCE 및 FOREIGN 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
속성을 사용하여 세 가지 방법을 시도했지만 아무것도 작동하지 않았습니다).