От одного до одного необязательного отношения, использующего Entity Framework Code First
Мы хотим использовать одно-одно необязательное отношение, используя First 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 должен иметь пользователя PII.
Мы пробовали эти быстрые методы подхода.
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 не создала никаких внешних ключей в этих двух таблицах.
Есть ли у вас идеи по этой проблеме?
Как мы можем создать одно-единственное необязательное отношение, используя сначала фреймворк сущности?
Ответы
Ответ 1
EF Code Сначала поддерживает отношения 1:1
и 1:0..1
. Последнее - это то, что вы ищете ( "от одного до нуля или одного" ).
Ваши попытки свободно говорить , требуемые на обоих концах, в одном случае и необязательно на обоих концах в другой.
То, что вам нужно, является необязательным на одном конце и требуется на другом.
Вот пример из книги программирования E.F. First Book
modelBuilder.Entity<PersonPhoto>()
.HasRequired(p => p.PhotoOf)
.WithOptional(p => p.Photo);
Объект PersonPhoto
имеет свойство навигации, называемое PhotoOf
, которое указывает на тип 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, отношения один к одному (или от одного до нуля/одного) являются одним из самых запутанных отношений для настройки кода сначала, поэтому вы не одиноки!:)
Ответ 2
У нас исходный код,
Просто выполните, если у вас есть отношения "один ко многим" между LoyaltyUserDetail
и PIIUser
, поэтому вы должны быть
modelBuilder.Entity<LoyaltyUserDetail>()
.HasRequired(m => m.PIIUser )
.WithMany()
.HasForeignKey(c => c.LoyaltyUserDetailId);
EF должен создать весь внешний ключ, который вам нужен, и просто не заботится о WithMany!
Ответ 3
В коде есть несколько ошибок.
A 1:1: либо PK < -PK, где одна сторона PK также является FK, или PK < -FK + UC, где сторона FK является не-PK и имеет UC. Ваш код показывает, что у вас есть FK < -FK, поскольку вы определяете обе стороны, чтобы иметь FK, но это неправильно. я recon PIIUser
- сторона PK, а LoyaltyUserDetail
- сторона FK. Это означает, что PIIUser
не имеет поля FK, но LoyaltyUserDetail
делает.
Если отношение 1:1 не является обязательным, сторона FK должна иметь по крайней мере одно поле с нулевым значением.
p.s.w.g. выше ответили на ваш вопрос, но сделали ошибку, что он также определил FK в PIIUser, что, конечно, неправильно, как я описал выше. Итак, определите нулевое поле FK в LoyaltyUserDetail
, определите атрибут в LoyaltyUserDetail
, чтобы пометить это поле FK, но не указывайте поле FK в PIIUser
.
Вы получаете исключение, описанное выше p.s.w.g. потому что ни одна сторона не является стороной ПК (принцип конца).
EF не очень хорош в 1:1, поскольку он не способен обрабатывать уникальные ограничения. Вначале я не эксперт по коду, поэтому я не знаю, может ли он создать UC или нет.
(edit) btw: A 1:1 B (FK) означает, что создано только 1 ограничение FK, на целевом B, указывающем на A PK, а не на 2.
Ответ 4
Попробуйте добавить атрибут 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; }
...
}
Ответ 5
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);
это решит проблему на ССЫЛКЕ и ИНОСТРАННЫЕ КЛАВИШИ
когда ОБНОВЛЕНИЕ или DELETING запись