web-dev-qa-db-fra.com

Comprendre d'abord l'attribut ForeignKey dans le code de structure d'entité

Voir le post suivant pour quelques informations:

Entité cadre un à zéro ou une relation sans propriété de navigation

J'avais toujours pensé que ForeignKey était utilisé pour montrer quelle propriété dans une classe contenait la ForeignKey qui déterminait la propriété de navigation, par exemple.

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; }
}

Cependant, j'ai découvert sur le post lié que ce n'est pas correct et que, comme la clé primaire de DeferredData s'appelait Id, j'avais réellement besoin:

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; }
}

c'est-à-dire que ForeignKey est utilisé pour pointer vers l'autre classe.

J'ai ensuite procédé à la modification de certaines des autres références:

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; }
}

Mais cela a échoué. Il s'est avéré que celui-ci ForeignKey devait pointer vers l'id sur la classe 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; }
}

Je suppose que c'est parce que cette deuxième relation est une à plusieurs alors que la première était de un à zéro ou un, et que la fin principale de la relation diffère, mais j'apprécierais une certaine clarté sur ce/références à de bons articles, donc je peux comprendre ce qui se passe et exactement ce que fait ForeignKey.

Je cherchais également de la clarté dans l'exemple ci-dessus sur la façon dont public int? DeferredDataId { get; set; } s'inscrit dans l'équation étant donné qu'il n'est pas explicitement lié à DeferredData. Je suis heureux que cela corresponde à la convention, mais comment pourrais-je le dire explicitement, par exemple si elle avait un nom différent? Tous les exemples que j'ai vus dans cette présentation sur l'utilisation de l'attribut ForeignKey mais cela ne peut pas être la réponse dans tous les cas ci-dessus!

Toutes les aides sont grandement appréciées - chercher à comprendre le problème plutôt qu'à résoudre un problème spécifique car j'ai beaucoup de références dans mon modèle, j'ai donc besoin d'établir quelle approche adopter avec chacun.

Merci.

Modifier

Ajout d'autres classes pour aider:

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
}
26
Mark007

Le côté requis de la relation 1..0 MemberDataSet ne doit pas avoir de FK à DeferredData. Au lieu de cela, le PK de DeferredData doit également être un FK à MemberDataSet (connu comme clé primaire partagée)

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 fluide:

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

Je pense que votre idée originale était correcte, à une légère exception près. En plaçant la clé étrangère sur le MemberDataSet, vous indiquez que vous souhaitez une relation zéro ou un à plusieurs.

Dans votre exemple, MemberDataSet.DeferredData est facultatif et DeferredData peut être référencé par de nombreuses instances de MemberDataSet.

Dans une syntaxe fluide, cela s'exprimerait par:

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

Afin d'en faire une propriété de un à zéro ou un, vous pouvez mettre une contrainte de clé unique (où non nulle) sur la colonne DeferredDataId de MemberDataSet . Cela signifierait qu'une entité DeferredData ne pourrait être référencée que par une seule entité MemberDataSet.

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

Remarque: ce type de clé filtrée n'est disponible que dans SQL Server 2008 et versions ultérieures.

0
David Kirkland

Je pense que vous aviez raison (à condition que je vous comprenne bien). [ForeignKeyAttribute] est utilisé sur la clé étrangère correspondante. Pas sur la clé primaire de votre objet lié.

This is my object, and the foreign key is DeferredDataId

Ni le Id dans votre objet n'est la clé étrangère (c'est la clé primaire) ni le ID de l'objet lié n'est la clé étrangère (c'est la clé primaire de l'autre côté de la relation)

J'espère que je vous ai bien compris :) Parce que je ne suis pas sûr.

0
csteinmueller