Je cherchais des ressources sur la façon de déclarer des relations de clé étrangère et d'autres contraintes en utilisant le code d'abord EF 4.1 sans trop de chance. Fondamentalement, je construis le modèle de données en code et utilise MVC3 pour interroger ce modèle. Tout fonctionne via MVC, ce qui est excellent (félicitations à Microsoft!), Mais maintenant, je ne veux pas que cela fonctionne, car j’ai besoin de contraintes de modèle de données.
Par exemple, j'ai un objet Order qui a une tonne de propriétés qui sont des objets externes (tables). À l'heure actuelle, je peux créer un ordre sans problème, mais sans pouvoir ajouter la clé étrangère ou des objets externes. MVC3 ne pose aucun problème.
Je réalise que je pourrais simplement ajouter les objets moi-même dans la classe du contrôleur avant de sauvegarder, mais j'aimerais que l'appel à DbContext.SaveChanges () échoue si les relations de contrainte ne sont pas respectées.
NOUVELLE INFORMATION
Ainsi, plus précisément, j'aimerais qu'une exception se produise lorsque je tente de sauvegarder un objet Commande sans spécifier d'objet client. Cela ne semble pas être le comportement si je compose simplement les objets comme décrit dans la plupart de la documentation Code First EF.
Dernier code:
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; }
}
Voici l'erreur que je reçois maintenant lorsque j'accède à la vue générée par le VS pour Patient:
MESSAGE D'ERREUR
La propriété ForeignKeyAttribute 'Patient' sur le type 'PhysicianPortal.Models.Order' n'est pas valide. Le nom de clé étrangère 'Parent' est introuvable dans le type dépendant 'PhysicianPortal.Models.Order'. La valeur Nom doit être une liste de noms de propriétés de clés étrangères, séparés par des virgules.
Cordialement,
Guido
Si vous avez une classe Order
, ajouter une propriété référençant une autre classe dans votre modèle, par exemple Customer
devrait suffire à faire savoir à EF qu'une relation existe entre eux:
public class Order
{
public int ID { get; set; }
// Some other properties
// Foreign key to customer
public virtual Customer Customer { get; set; }
}
Vous pouvez toujours définir explicitement la relation FK
:
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; }
}
Le constructeur ForeignKeyAttribute
prend une chaîne en tant que paramètre: si vous la placez sur une propriété de clé étrangère, elle représente le nom de la propriété de navigation associée. Si vous le placez sur la propriété de navigation, il représente le nom de la clé étrangère associée.
Cela signifie que si vous placez le ForeignKeyAttribute
sur la propriété Customer
, l'attribut prendrait CustomerID
dans le constructeur:
public string CustomerID { get; set; }
[ForeignKey("CustomerID")]
public virtual Customer Customer { get; set; }
EDIT basé sur Latest Code Vous obtenez cette erreur à cause de cette ligne:
[ForeignKey("Parent")]
public Patient Patient { get; set; }
EF cherchera une propriété appelée Parent
à utiliser comme applicateur de clé étrangère. Vous pouvez faire 2 choses:
1) Supprimez le ForeignKeyAttribute
et remplacez-le par le RequiredAttribute
pour marquer la relation comme requis:
[Required]
public virtual Patient Patient { get; set; }
Décorer une propriété avec le RequiredAttribute
a aussi un effet secondaire: la relation dans la base de données est créée avec ON DELETE CASCADE
.
Je recommanderais également de rendre la propriété virtual
pour activer le chargement différé.
2) Créez une propriété appelée Parent
qui servira de clé étrangère. Dans ce cas, il est probablement plus logique de l'appeler par exemple ParentID
(vous devrez également modifier le nom dans le ForeignKeyAttribute
):
public int ParentID { get; set; }
D'après mon expérience dans ce cas, cependant, cela fonctionne mieux de l'inverser:
[ForeignKey("Patient")]
public int ParentID { get; set; }
public virtual Patient Patient { get; set; }
Vous pouvez définir une clé étrangère en:
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; }
}
Maintenant, ParentId est une propriété de clé étrangère et définit la relation requise entre l'enfant et le parent existant. L'enregistrement de l'enfant sans parent sortant générera une exception.
Si le nom de votre propriété FK ne comprend pas le nom de la propriété de navigation et le nom de la clé PK parent, vous devez utiliser une annotation de données ForeignKeyAttribute ou une API fluide pour mapper la relation.
Annotation des données:
// The name of related navigation property
[ForeignKey("Parent")]
public int ParentId { get; set; }
API Fluent:
modelBuilder.Entity<Child>()
.HasRequired(c => c.Parent)
.WithMany(p => p.Childs)
.HasForeignKey(c => c.ParentId);
D'autres types de contraintes peuvent être appliqués par annotations de données et validation de modèle .
Modifier:
Vous obtiendrez une exception si vous ne définissez pas ParentId
. C'est une propriété obligatoire (non nullable). Si vous ne le définissez pas, il essaiera probablement d'envoyer la valeur par défaut à la base de données. La valeur par défaut est 0, donc si vous n'avez pas de client avec Id = 0, vous obtiendrez une exception.