web-dev-qa-db-fra.com

Entity Framework: J'ai défini la clé étrangère, SaveChanges puis accéder à la propriété de navigation, mais elle ne charge pas l'entité associée. Pourquoi pas?

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

35
Colin

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é).

54
Slauma

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.

10
nick_w

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.

0
w.brian