От одного до одного необязательного отношения, использующего Entity Framework Code First

326
30

Мы хотим использовать одно-одно необязательное отношение, используя 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 не создала никаких внешних ключей в этих двух таблицах.


Есть ли у вас идеи по этой проблеме?
Как мы можем создать одно-единственное необязательное отношение, используя сначала фреймворк сущности?

спросил(а) 2021-01-19T16:54:45+03:00 6 месяцев, 2 недели назад
1
Решение
386

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, отношения один к одному (или от одного до нуля/одного) являются одним из самых запутанных отношений для настройки кода сначала, поэтому вы не одиноки!:)

ответил(а) 2021-01-19T16:54:45+03:00 6 месяцев, 2 недели назад
186

У нас исходный код,


Просто выполните, если у вас есть отношения "один ко многим" между LoyaltyUserDetail и PIIUser, поэтому вы должны быть

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

EF должен создать весь внешний ключ, который вам нужен, и просто не заботится о WithMany!

ответил(а) 2021-01-19T16:54:45+03:00 6 месяцев, 2 недели назад
89

В коде есть несколько ошибок.


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.

ответил(а) 2021-01-19T16:54:45+03:00 6 месяцев, 2 недели назад
78

Попробуйте добавить атрибут 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; }
...
}

ответил(а) 2021-01-19T16:54:45+03:00 6 месяцев, 2 недели назад
45

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 запись

ответил(а) 2021-01-19T16:54:45+03:00 6 месяцев, 2 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема