J'utilise cette classe d'entité avec Entity Framework 5 Code First:
public class Survey
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string SurveyName { get; set; }
[Required]
public int ClientID { get; set; }
[ForeignKey("ClientID")]
public virtual Client Client { get; set; }
}
Et dans la méthode Create de mon contrôleur, je fais ceci:
Survey entity = new Survey()
{
SurveyName = "Test Name",
ClientID = 4
};
db.Surveys.Add(entity);
db.SaveChanges();
Client c1 = entity.Client; //Why is this null?
Client c2 = db.Clients.Find(entity.ClientID); //But this isn't?
string s2 = c2.ClientName;
string s1 = c1.ClientName; //null reference thrown here
La propriété de navigation du client reste nulle après SaveChanges. Je m'attendais à ce que l'appel charge le client à partir de la base de données car la clé étrangère existe. Pourquoi n'a-t-il pas fait ça?
EDIT Le code ici provient de l'époque où mes contrôleurs étaient dépendants de DbContext
. Peu de temps après que cela fonctionne, j'ai modifié le code pour utiliser des référentiels et une unité de travail. Une partie de cette décision était motivée par le fait qu’il était injuste d’utiliser Create
quand je voulais utiliser new
. Ce qui s’est alors passé, c’est que j’ai rencontré un problème avec comment faire en sorte que les mandataires soient créés lors de l’utilisation du modèle de référentiel .
Pour vous assurer que le chargement paresseux d'une propriété de navigation fonctionnera après la création du parent, vous ne devez pas créer la variable Survey
avec l'opérateur new
, mais la créer à l'aide de l'instance de contexte, car elle instanciera un proxy dynamique capable de charger paresseusement. le Client
connexe. C'est ce que la méthode DbSet<T>.Create()
est pour:
Survey entity = db.Surveys.Create();
entity.SurveyName = "Test Name";
entity.ClientID = 4;
db.Surveys.Add(entity);
db.SaveChanges();
Client c1 = entity.Client;
string s1 = c1.ClientName;
// will work now if a Client with ID 4 exists in the DB
Soulignons simplement que ce n'est pas la ligne entity.ClientID = 4;
ou db.Surveys.Add(entity);
ou db.SaveChanges
qui charge le client à partir de la base de données, mais la ligne Client c1 = entity.Client;
(chargement différé).
Comme @NicholasButler l'a dit, l'appel de SaveChanges
fait ce qu'il dit sur l'étain - vous pouvez le voir si vous déboguez votre code: la sortie Intellitrace indiquera le code SQL généré pour l'insertion/la mise à jour que vous persistez, mais il n'y aura pas de modification ultérieure. sélectionner.
N'oubliez pas que, sauf si vous chargez avec impatience (à l'aide de la méthode Include
), les entités associées ne sont pas chargées lors de l'extraction. Il est donc raisonnable de penser que leur création/mise à jour ne le serait pas non plus.
Entity Framework (à partir des versions 4.1 et supérieures, je pense) prend en charge le chargement différé. Cela signifie que s'il est activé, un code comme Client c1 = entity.Client;
devrait charger cet objet Client
. Pour être clair, cette opération n'est pas directement liée à l'appel SaveChanges
.
Il serait utile de vérifier si db.Configuration.LazyLoadingEnabled
est défini sur true
. Si ce n'est pas le cas, essayez de le définir sur true
et voyez si Client c1 = entity.Client;
est toujours nul.
En bref, l'appel de SaveChanges
ne déclenche pas de chargement, mais si le chargement paresseux est activé, l'accès à entity.Client
devrait déclencher un chargement de l'entité si elle n'a pas déjà été chargée.
Modifier:
J'aurais dû le savoir plus tôt, mais vous ne risquez pas de charger paresseux sur votre objet Survey entity
. La raison en est que EF exploite sa magie de chargement paresseux en créant une classe dérivée de votre classe mais en remplaçant les propriétés marquées par virtual
pour prendre en charge le chargement paresseux. Cela se produit lorsque vous effectuez une récupération, de sorte que votre objet entity
ne charge pas paresseux quoi que ce soit en l'état.
Essayez ceci juste après votre appel à SaveChanges
:
Survey entity2 = db.Surveys.Find(entity.ID);
Client c1 = entity2.Client;
Cela devrait montrer le comportement que vous êtes après.
Pour activer le chargement paresseux, vous devez définir toutes les propriétés de la classe Survey
en tant que virtuelles.
Voir http://msdn.Microsoft.com/en-us/library/vstudio/dd468057(v=vs.100).aspx pour plus d'informations.