Понимание атрибута ForeignKey в коде структуры сущности первым

Смотрите следующий пост для некоторого фона:

Структура сущности от одного до нуля или одно отношение без свойства навигации

Я всегда думал, что ForeignKey был использован, чтобы показать, какое свойство в классе содержит ForeignKey, который определил свойство навигации, например

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("DeferredDataId")]
    public virtual DeferredData DeferredData { get; set; }
}

Тем не менее, я обнаружил в связанном посте, что это неправильно и что первичный ключ DeferredData назывался Id, который мне действительно был нужен:

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("Id")]
    public virtual DeferredData DeferredData { get; set; }
}

т.е. ForeignKey используется для указания на другой класс.

Затем я приступил к изменению некоторых других ссылок:

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("Id")]
    public virtual DeferredData DeferredData { get; set; }

    public int? SignedOffById { get; set; }
    [ForeignKey("UserId")]
    public virtual UserProfile SignedOffBy { get; set; }
}

Однако это не удалось. Оказалось на этом ForeignKey необходимо указать на Id на MemberDataSet учебный класс.

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public int? DeferredDataId { get; set; }
    [ForeignKey("Id")]
    public virtual DeferredData DeferredData { get; set; }

    public int? SignedOffById { get; set; }
    [ForeignKey("SignedOffById")]
    public virtual UserProfile SignedOffBy { get; set; }
}

Я предполагаю, что это потому, что это второе отношение "один ко многим", тогда как первое было "один к нулю" или "один к одному", и это фактически означает, что основной конец отношения отличается, но я был бы признателен за некоторую ясность в этом / ссылки на хорошие статьи, так что я могу понять, что происходит и что именно делает ForeignKey.

Я также искал ясности в приведенном выше примере того, как public int? DeferredDataId { get; set; } вписывается в уравнение, если оно явно не связано с DeferredData, Я рад, что это будет соответствовать соглашению, но как бы я сказал об этом, например, если бы у него было другое имя? Все примеры, которые я видел в этом разговоре об использовании ForeignKey атрибут, но это не может быть ответом во всех случаях, указанных выше!

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

Благодарю.

редактировать

Добавлены другие классы, чтобы помочь:

public class DeferredData
{

    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    //other properties
}

public class UserProfile
{

    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }

    //other properties
}

3 ответа

Решение

Требуемая сторона отношения 1..0 MemberDataSet не должен иметь ФК для DeferredData, Вместо, DeferredDataПК также должен быть FK для MemberDataSet (известный как общий первичный ключ)

public class MemberDataSet
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public virtual DeferredData DeferredData { get; set; }
}

public class DeferredData
{
    // DeferredData.Id is both the PK and a FK to MemberDataSet
    [Key]
    [DatabaseGenerated( DatabaseGeneratedOption.None )]
    [ForeignKey( "MemberDataSet" )]
    public int Id { get; set; }

    [Required]
    public virtual MemberDataSet MemberDataSet { get; set; }
}

Свободный API:

modelBuilder.Entity<MemberDataSet>()
    .HasOptional( mds => mds.DeferredData )
    .WithRequired()
    .WillCascadeOnDelete();

Я думаю, что вы были правы (при условии, что я вас правильно понимаю). [ForeignKeyAttribute] используется по основному внешнему ключу. Не по первичному ключу вашего связанного объекта.

This is my object, and the foreign key is DeferredDataId

Ни Id внутри вашего объекта есть внешний ключ (это первичный ключ), ни ID связанного объекта является внешним ключом (это первичный ключ другой стороны отношения)

Надеюсь, я вас правильно понял:) Потому что я не уверен.

Я думаю, что ваша первоначальная идея была правильной, за одним небольшим исключением. Поместив внешний ключ на MemberDataSet вы намекаете, что хотели бы иметь отношения ноль или один-ко-многим.

В вашем примере MemberDataSet.DeferredData необязательно, и DeferredData могут быть упомянуты многими MemberDataSet экземпляров.

В свободном синтаксисе это будет выражаться как:

modelBuilder.Entity<MemberDataSet>()
    .HasOptional(dataSet => dataSet.DeferredData)
    .WithMany()
    .HasForeignKey(deferredData => deferredData.DeferredDataId);

Чтобы сделать это свойство "один к нулю или один", вы можете наложить уникальное (где не нулевое) ключевое ограничение на столбец DeferredDataId MemberDataSet. Это будет означать, что на объект DeferredData может ссылаться только один объект MemberDataSet.

CREATE UNIQUE INDEX unique_MemberDataSet_DeferredDataId ON MemberDataSet(DeferredDataId) WHERE DeferredDataId IS NOT NULL

Примечание. Этот тип отфильтрованного ключа доступен только в SQL Server 2008 и более поздних версиях.

Другие вопросы по тегам