2011-04-04 27 views
86

He estado buscando recursos sobre cómo declarar relaciones de claves foráneas y otras restricciones usando código primero 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.¿Cómo debo declarar relaciones de claves foráneas usando Code First Entity Framework (4.1) en MVC3?

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 di cuenta de que solo podía agregar los objetos yo mismo en la clase de controlador antes de guardar, pero me gustaría que la llamada a DbContext.SaveChanges() fallara si las relaciones de restricción no se han cumplido.

NUEVA INFORMACIÓN

Así, en concreto, me gustaría un excepción a ocurrir cuando intento guardar un objeto Orden sin especificar un objeto de cliente. Este no parece ser el comportamiento si I solo compone los objetos como se describe en la mayoría de la documentación de Code First EF.

reciente 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 consigo ahora, cuando el acceso a VS Vista generada por el paciente:

MENSAJE DE ERROR

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

Saludos,

Guido

Respuesta

146

Si usted tiene una clase Order, añadiendo una propiedad que hace referencia a otra clase en su modelo, por ejemplo Customer debería ser suficiente para que EF sabe que hay una relación en la que hay :

public class Order 
{ 
    public int ID { get; set; } 

    // Some other properties 

    // Foreign key to customer 
    public virtual Customer Customer { get; set; } 
} 

siempre se 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, si usted dónde colocar el ForeignKeyAttribute en la propiedad 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 último código 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. Puede hacer 2 cosas:

1) Retire el ForeignKeyAttribute y reemplazarlo con el RequiredAttribute para marcar la relación según sea necesario:

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

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

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

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

public int ParentID { get; set; } 

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

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

public virtual Patient Patient { get; set; } 
+0

Gracias Sergi - He añadido algo de información adicional en el presupuesto del bloque. –

+0

@Guido: he actualizado mi respuesta en función de la última edición del código, espero que esto ayude. –

+1

¡Bien hecho Sergi! – Shimmy

27

se puede definir la clave externa por:

public class Parent 
{ 
    public int Id { get; set; } 
    public virtual ICollection<Child> 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 propiedad de clave externa y define relación entre requerido hijo y padre existente. Al guardar al niño sin expulsar al padre se lanzará una excepción.

Si el nombre de la propiedad FK no consiste en el nombre de la propiedad de navegación y el nombre del padre PK debe utilizar cualquiera de anotación de datos ForeignKeyAttribute o API fluida para mapear la relación

anotación de datos:

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

API fluida:

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

Otros tipos de restricciones puede ser aplicada por data annotations and model validation.

Editar:

Usted recibirá una excepción si no se establece 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.

+0

Gracias Ladislav - He añadido algo de información adicional en el presupuesto del bloque. –

+0

@Guido: Editado mi respuesta. –

+0

@Ladislav. Entonces, para hacer cumplir esta restricción, DEBO tener tanto la referencia a Parent como una referencia a ParentId. ¿Es eso correcto? Agregaré la clase actual de arriba para referencia. –

Cuestiones relacionadas