Один к одному необязательные отношения с использованием Entity Framework Fluent API
Сначала мы хотим использовать одно необязательное отношение, используя код Entity Framework. У нас две сущности.
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 не создал никаких внешних ключей в этих 2 таблицы.
у вас есть идеи по этому вопросу? Как мы можем создать одно необязательное отношение с помощью Entity framework fluent api?
6 ответов
код EF сначала поддерживает 1:1
и 1:0..1
отношения. Последнее - это то, что вы ищете ("от одного до нуля или единицы").
ваши попытки свободно говорят требуются на обоих концах в одном случае и дополнительно на обоих концах в другой.
что нужно дополнительно и требуются С другой стороны.
вот пример из первой книги программирования E. F. Code
modelBuilder.Entity<PersonPhoto>()
.HasRequired(p => p.PhotoOf)
.WithOptional(p => p.Photo);
на PersonPhoto
сущность имеет свойство навигации PhotoOf
это указывает на Person
тип. The Person
type имеет свойство навигации с именем Photo
к PersonPhoto
тип.
в двух связанных классах вы используетепервичный ключ, а не внешние ключи. т. е. вы не будете использовать LoyaltyUserDetailId
или PIIUserId
свойства. Вместо этого отношение зависит от Id
поля обоих типов.
если вы используете fluent 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's LoyaltyUserDetail
свойство является необязательным и LoyaltyUser по свойства.
вы всегда должны использовать шаблон имеет / С.
HTH и FWIW, один к одному (или один к нулю/один) отношения являются одним из самых запутанных отношений для настройки в коде сначала, так что вы не одиноки! :)
просто сделайте, как если бы у вас были отношения один ко многим между LoyaltyUserDetail
и PIIUser
Итак, вы отображение должно быть
modelBuilder.Entity<LoyaltyUserDetail>()
.HasRequired(m => m.PIIUser )
.WithMany()
.HasForeignKey(c => c.LoyaltyUserDetailId);
EF должен создать весь внешний ключ, который вам нужен, и просто не заботьтесь о WithMany !
в вашем коде есть несколько ошибок.
A 1:1 отношения-это: PK, где одна сторона PK также является FK, или PK, где сторона FK не является PK и имеет UC. Ваш код показывает, что у вас есть FK, поскольку вы определяете обе стороны, чтобы иметь FK, но это неправильно. Я Рекон PIIUser
является стороной ПК и LoyaltyUserDetail
является стороной FK. Это значит PIIUser
не имеет поля FK, но LoyaltyUserDetail
делает.
если 1:1 отношение является необязательным, сторона FK должна иметь по крайней мере 1 nullable поле.
p.s.w.г. выше ответил на ваш вопрос, но сделал ошибку, Что s/he также определил 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, указывающей на PK A, а не 2.
попробуйте добавить до 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; }
...
}
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);
это решит проблему на ссылка и ВНЕШНИЕ КЛЮЧИ
, когда обновление или удаление запись
одна вещь, которая путает с вышеуказанными решениями, заключается в том, что первичный ключ определяется как "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);
сделал бы трюк, принятое решение терпит неудачу чтобы четко объяснить это, и это отбросило меня на несколько часов, чтобы найти причину