¿Cómo debo declarar relaciones de claves foráneas usando Code First Entity Framework (4.1) en MVC3?

He estado buscando recursos sobre cómo declarar relaciones de claves externas y otras restricciones usando primero el código EF 4.1 sin mucha suerte. Básicamente estoy construyendo el modelo de datos en código y usando MVC3 para consultar ese modelo. Todo funciona a través de MVC, que es genial (felicitaciones a Microsoft!) Pero ahora quiero que NO funcione porque necesito tener restricciones en el modelo de datos.

Por ejemplo, tengo un objeto Order que tiene un montón de propiedades que son objetos externos (tablas). En este momento puedo crear un pedido sin problemas, pero sin poder agregar la clave externa u objetos externos. MVC3 establece esto sin problema.

Me doy cuenta de que solo podría agregar los objetos yo mismo en la clase de controlador antes de guardar, pero me gustaría que la llamada a DbContext.SaveChanges () falle si no se cumplen las relaciones de restricción.

NUEVA INFORMACIÓN

Entonces, específicamente, me gustaría que ocurriera una excepción cuando bash guardar un objeto Order sin especificar un objeto cliente. Este no parece ser el comportamiento si solo hago los objetos como se describe en la mayoría de la documentación de Code First EF.

Último código:

public class Order { public int Id { get; set; } [ForeignKey( "Parent" )] public Patient Patient { get; set; } [ForeignKey("CertificationPeriod")] public CertificationPeriod CertificationPeriod { get; set; } [ForeignKey("Agency")] public Agency Agency { get; set; } [ForeignKey("Diagnosis")] public Diagnosis PrimaryDiagnosis { get; set; } [ForeignKey("OrderApprovalStatus")] public OrderApprovalStatus ApprovalStatus { get; set; } [ForeignKey("User")] public User User { get; set; } [ForeignKey("User")] public User Submitter { get; set; } public DateTime ApprovalDate { get; set; } public DateTime SubmittedDate { get; set; } public Boolean IsDeprecated { get; set; } } 

Este es el error que obtengo ahora al acceder a la vista generada VS para Paciente:

MENSAJE DE ERROR

ForeignKeyAttribute en la propiedad ‘Paciente’ en el tipo ‘PhysicianPortal.Models.Order’ no es válido. El nombre de la clave externa ‘Parent’ no se encontró en el tipo dependiente ‘PhysicianPortal.Models.Order’. El valor Name debe ser una lista separada por comas de nombres de propiedades de claves foráneas.

Saludos,

Guido

Si tiene una clase de Order , agregando una propiedad que hace referencia a otra clase en su modelo, por ejemplo, el Customer debería ser suficiente para hacerle saber a EF que hay una relación allí:

 public class Order { public int ID { get; set; } // Some other properties // Foreign key to customer public virtual Customer Customer { get; set; } } 

Siempre puede establecer la relación FK explícitamente:

 public class Order { public int ID { get; set; } // Some other properties // Foreign key to customer [ForeignKey("Customer")] public string CustomerID { get; set; } public virtual Customer Customer { get; set; } } 

El constructor ForeignKeyAttribute toma una cadena como parámetro: si la coloca en una propiedad de clave externa, representa el nombre de la propiedad de navegación asociada. Si lo coloca en la propiedad de navegación, representa el nombre de la clave externa asociada.

Lo que esto significa es que si coloca el ForeignKeyAttribute en la propiedad del Customer , el atributo tomaría CustomerID en el constructor:

 public string CustomerID { get; set; } [ForeignKey("CustomerID")] public virtual Customer Customer { get; set; } 

EDITAR basado en el código más reciente Obtiene ese error debido a esta línea:

 [ForeignKey("Parent")] public Patient Patient { get; set; } 

EF buscará una propiedad llamada Parent para usarla como el ejecutor de clave foránea. Puedes hacer 2 cosas:

1) Elimine ForeignKeyAttribute y reemplácelo con RequiredAttribute para marcar la relación según sea necesario:

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

Decorar una propiedad con RequiredAttribute también tiene un bonito efecto secundario: la relación en la base de datos se crea con ON DELETE CASCADE .

También recomendaría hacer que la propiedad sea virtual para permitir la carga diferida.

2) Crea una propiedad llamada Parent que servirá como clave externa. En ese caso, probablemente tenga más sentido llamarlo, por ejemplo, ParentID (también deberá cambiar el nombre en ForeignKeyAttribute ):

 public int ParentID { get; set; } 

En mi experiencia en este caso, aunque funciona mejor tenerlo al revés:

 [ForeignKey("Patient")] public int ParentID { get; set; } public virtual Patient Patient { get; set; } 

Puede definir clave foránea por:

 public class Parent { public int Id { get; set; } public virtual ICollection Childs { get; set; } } public class Child { public int Id { get; set; } // This will be recognized as FK by NavigationPropertyNameForeignKeyDiscoveryConvention public int ParentId { get; set; } public virtual Parent Parent { get; set; } } 

Ahora ParentId es una propiedad de clave externa y define la relación requerida entre el hijo y el padre existente. Al guardar al niño sin expulsar al padre se lanzará una excepción.

Si su nombre de propiedad FK no consta del nombre de la propiedad de navegación y el nombre principal de PK, debe usar la anotación de datos ForeignKeyAttribute o la API con fluidez para mapear la relación.

Anotación de datos:

 // The name of related navigation property [ForeignKey("Parent")] public int ParentId { get; set; } 

Fluent API:

 modelBuilder.Entity() .HasRequired(c => c.Parent) .WithMany(p => p.Childs) .HasForeignKey(c => c.ParentId); 

Se pueden aplicar otros tipos de restricciones mediante anotaciones de datos y validación de modelos .

Editar:

Obtendrá una excepción si no configura ParentId . Es propiedad requerida (no anulable). Si simplemente no lo configura, lo más probable es que intente enviar el valor predeterminado a la base de datos. El valor predeterminado es 0, por lo que si no tiene un cliente con Id = 0, obtendrá una excepción.