Один к одному необязательные отношения с использованием 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);

сделал бы трюк, принятое решение терпит неудачу чтобы четко объяснить это, и это отбросило меня на несколько часов, чтобы найти причину