Chcemy użyć relacji jeden do jednej opcjonalnej przy użyciu Entity Framework Code First. Mamy dwie istoty.
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
może mieć, LoyaltyUserDetail
ale LoyaltyUserDetail
musi mieć PIIUser
. Wypróbowaliśmy te techniki płynnego podejścia.
modelBuilder.Entity<PIIUser>()
.HasOptional(t => t.LoyaltyUserDetail)
.WithOptionalPrincipal(t => t.PIIUser)
.WillCascadeOnDelete(true);
To podejście nie spowodowało utworzenia LoyaltyUserDetailId
klucza obcego w PIIUsers
tabeli.
Następnie wypróbowaliśmy następujący kod.
modelBuilder.Entity<LoyaltyUserDetail>()
.HasRequired(t => t.PIIUser)
.WithRequiredDependent(t => t.LoyaltyUserDetail);
Ale tym razem EF nie utworzył żadnych kluczy obcych w tych 2 tabelach.
Czy masz jakieś pomysły na ten numer? Jak możemy utworzyć relację jeden do jednego, używając Fluent API platformy encji?
źródło
WithOptional
? Czym różni się odWithRequiredDependent
iWithRequiredOptional
?Po prostu zrób tak, jeśli masz relację jeden do wielu między,
LoyaltyUserDetail
aPIIUser
więc mapowanie powinno byćEF powinien utworzyć wszystkie potrzebne klucze obce i po prostu nie przejmować się WithMany !
źródło
Z Twoim kodem jest kilka błędów.
1: 1 związku jest: PK <-PK , gdzie jedna strona PK również FK lub PK <-FK + UC , gdzie boczny FK jest nie-PK i ma UC. Twój kod pokazuje, że masz FK <-FK , ponieważ definiujesz obie strony, aby miały FK, ale to źle. Ja rekonesans
PIIUser
to strona PK iLoyaltyUserDetail
strona FK. Oznacza to,PIIUser
że nie ma pola FK, aleLoyaltyUserDetail
ma.Jeśli relacja 1: 1 jest opcjonalna, strona FK musi mieć co najmniej 1 pole dopuszczające wartość null.
pswg powyżej odpowiedział na twoje pytanie, ale popełnił błąd, że zdefiniował również FK w PIIUser, co jest oczywiście błędne, jak opisałem powyżej. Zdefiniuj więc pole FK dopuszczające wartość null w
LoyaltyUserDetail
, zdefiniuj atrybut w,LoyaltyUserDetail
aby oznaczyć go jako pole FK, ale nie określaj pola FK wPIIUser
.Dostajesz wyjątek, który opisałeś powyżej pod postem pswg, ponieważ żadna strona nie jest stroną PK (koniec zasady).
EF nie jest zbyt dobry w stosunku 1: 1, ponieważ nie jest w stanie obsłużyć unikalnych ograniczeń. Nie jestem najpierw ekspertem od kodu, więc nie wiem, czy jest on w stanie stworzyć UC, czy nie.
(edytuj) btw: A 1: 1 B (FK) oznacza, że zostało utworzone tylko 1 ograniczenie FK, na celu B wskazującym na PK A, a nie 2.
źródło
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);
to rozwiąże problem na ODNIESIENIU i KLUCZACH ZAGRANICZNYCH
podczas AKTUALIZACJI lub USUWANIA rekordu
źródło
Spróbuj dodać
ForeignKey
atrybut doLoyaltyUserDetail
właściwości:public class PIIUser { ... public int? LoyaltyUserDetailId { get; set; } [ForeignKey("LoyaltyUserDetailId")] public LoyaltyUserDetail LoyaltyUserDetail { get; set; } ... }
A
PIIUser
nieruchomość:public class LoyaltyUserDetail { ... public int PIIUserId { get; set; } [ForeignKey("PIIUserId")] public PIIUser PIIUser { get; set; } ... }
źródło
ForeignKey
atrybut tylko na jednym końcu relacji? czyli tylko naPIIUser
lubLoyaltyUserDetail
.Jedyną rzeczą, która jest myląca z powyższymi rozwiązaniami, jest to, że klucz podstawowy jest zdefiniowany jako „ Id” w obu tabelach i jeśli masz klucz podstawowy oparty na nazwie tabeli, to nie zadziała, zmodyfikowałem klasy, aby to zilustrować, tj. opcjonalna tabela nie powinna definiować swojego własnego klucza podstawowego, zamiast tego powinna używać tej samej nazwy klucza z tabeli głównej.
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; } }
A potem
Zrobiłoby to, przyjęte rozwiązanie nie wyjaśnia tego jasno i wyrzuciło mnie na kilka godzin, aby znaleźć przyczynę
źródło
Nie przydaje się to w przypadku oryginalnego plakatu, ale dla każdego, kto nadal korzysta z EF6, który potrzebuje klucza obcego innego niż klucz podstawowy, oto jak to zrobić:
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"));
Zwróć uwagę, że nie możesz użyć tego
LoyaltyUserDetailId
pola, ponieważ o ile wiem, można je określić tylko za pomocą interfejsu API Fluent. (Wypróbowałem trzy sposoby zrobienia tego za pomocąForeignKey
atrybutu i żaden z nich nie działał).źródło