primary name framework foreign first data column code c# entity-framework foreign-keys code-first shared-primary-key

name - foreign key entity c#



Entender el atributo ForeignKey en el código del marco de la entidad primero (3)

Ver la siguiente publicación para algunos antecedentes:

Marco de entidad de una a cero o una relación sin propiedad de navegación

Siempre pensé que ForeignKey se usaba para mostrar qué propiedad de una clase tenía ForeignKey que determinaba la propiedad de navegación, por ejemplo

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

Sin embargo, descubrí en la publicación vinculada que esto no es correcto y que como la clave principal de DeferredData se llamaba Id, realmente necesitaba:

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

es decir, ForeignKey se usa para señalar a la otra clase.

Luego procedí a cambiar algunas de las otras referencias:

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

Sin embargo, esto falló. Resultó que en este caso ForeignKey necesitaba apuntar al Id en la clase 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; } }

Supongo que esto se debe a que esta segunda relación es de uno a muchos, mientras que la primera fue de uno a cero o uno, y que efectivamente el final principal de la relación es diferente, pero agradecería algo de claridad sobre esto / referencias a buenos artículos, así puedo Entiende lo que está sucediendo y exactamente lo que ForeignKey está haciendo.

También estaba buscando claridad en el ejemplo anterior de cómo public int? DeferredDataId { get; set; } public int? DeferredDataId { get; set; } public int? DeferredDataId { get; set; } encaja en la ecuación dado que no está explícitamente vinculada a DeferredData . Estoy feliz de que esto coincida con la convención, pero ¿cómo lo diría explícitamente, por ejemplo, si tuviera un nombre diferente? Todos los ejemplos que he visto en esta charla sobre el uso del atributo ForeignKey pero esta no puede ser la respuesta en todos los casos por encima.

Toda la ayuda fue muy apreciada: buscaba comprender el problema en lugar de solucionar un problema específico, ya que tengo muchas referencias en mi modelo, así que necesito establecer qué enfoque tomar con cada una.

Gracias.

Editar

Se agregaron otras clases para ayudar:

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 }


Creo que estabas en lo correcto (siempre que te entiendo bien). [ForeignKeyAttribute] se usa en la clave externa correspondiente. No en la clave principal de su objeto vinculado.

This is my object, and the foreign key is DeferredDataId

Ni el Id su objeto es la clave externa (su clave principal) ni el ID del objeto vinculado es la clave externa (es la clave primaria del otro lado de la relación)

Espero que te haya entendido bien :) Porque no estoy seguro.


Creo que su idea original era correcta, con una pequeña excepción. Al colocar la clave externa en MemberDataSet está MemberDataSet a entender que desea una relación de cero o de uno a muchos.

En su ejemplo, MemberDataSet.DeferredData es opcional, y muchas instancias de MemberDataSet pueden hacer referencia a MemberDataSet .

En una sintaxis fluida esto se expresaría por:

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

Para hacer de esto una propiedad de uno a cero o uno, puede poner una restricción de clave única (donde no sea nula) en la columna DeferredDataId de MemberDataSet. Esto significaría que una única entidad MemberDataSet solo podría referir a una entidad DeferredData.

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

Nota: Este tipo de clave filtrada solo está disponible en SQL Server 2008 y posteriores.


El lado requerido de la relación MemberDataSet no debe tener un FK a DeferredData . En cambio, PK de DeferredData también debería ser un FK para MemberDataSet (conocido como clave primaria compartida)

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

Fluent API:

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