Один к одному необязательное отношение с помощью 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 61

6 ответов:

EF код сначала поддерживает 1:1 и 1:0..1 отношения. Последняя является то, что вы ищете ("один к нулю или одному").

ваши попытки свободно говорить требуются на обоих концах в одном случае и дополнительно на обоих концах в другой.

что нужно дополнительно и требуются С другой стороны.

вот пример из первой книги кода программирования E. F.

modelBuilder.Entity<PersonPhoto>()
.HasRequired(p => p.PhotoOf)
.WithOptional(p => p.Photo);

The PersonPhoto сущность имеет свойство навигации под названием PhotoOf это указывает на Person тип. Элемент Person тип имеет свойство навигации под названием 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 поле с нулевым значением.

P.s.w.g. выше ответил на ваш вопрос, но сделал ошибку, Что s/he также определил FK в PIIUser, что, конечно, неправильно, как я описал выше. поэтому определите поле FK с нулевым значением в LoyaltyUserDetail определите атрибут LoyaltyUserDetail чтобы отметить это поле FK, но не указывайте поле FK в PIIUser.

вы получаете исключение опишите выше ниже поста p.s.w.g., потому что ни одна сторона не является стороной PK (принцип конца).

EF не очень хорош в 1: 1, поскольку он не может обрабатывать уникальные ограничения. я не эксперт по коду во-первых, поэтому я не знаю, Может ли он создать UC или нет.

(edit) кстати: 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);

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