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

316
29

Мы хотим использовать одно-одно необязательное отношение, используя 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 2 месяца, 4 недели назад
1
Решение
373

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 2 месяца, 4 недели назад
181

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


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

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

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

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

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


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 2 месяца, 4 недели назад
76

Попробуйте добавить атрибут 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 2 месяца, 4 недели назад
44

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 2 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

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