web-dev-qa-db-fra.com

Entity Framework Include () ne fonctionne pas

J'ai la requête EF suivante:

TestEntities db = new TestEntities();
var questions = from q in db.Questions.Include("QuestionType")
                from sq in db.SurveyQuestions
                where sq.Survey == surveyTypeID
                orderby sq.Order
                select q;

foreach( var question in questions ) {
    // ERROR: Null Reference Exception
    Console.WriteLine("Question Type: " + question.QuestionType.Description);
}

J'obtiens une exception de référence nulle lorsque j'accède à la propriété QuestionType. J'utilise Include ("QuestionType") mais cela ne semble pas fonctionner. Qu'est-ce que je fais mal?

Edit: Il ne lance pas d'exception de référence nulle lorsque le chargement paresseux est activé.

Edit: Include () semble fonctionner quand je fais ce qui suit:

var questions = db.Questions.Include("QuestionType").Select(q => q);

Lorsque je prêche sur une entité distincte, Include semble échouer. N'est-ce pas autorisé lors de l'utilisation de Inclure? Qu'en est-il de ma requête qui empêche cette chose de fonctionner?

44
Dismissile

Le problème peut être lié à la sous-requête de votre expression Linq. Les sous-sélections, le regroupement et les projections peuvent entraîner l'échec silencieux du chargement avec Include, comme mentionné ici et expliqué plus en détail ici (voir les réponses de Diego Vega quelque part au milieu du fil).

Bien que je ne puisse pas vraiment voir que vous violez l'une des règles à suivre lors de l'utilisation de Include comme décrit dans ces messages, vous pouvez essayer de modifier la requête conformément à la recommandation:

var questions = from q in db.Questions
                from sq in db.SurveyQuestions
                where sq.Survey == surveyTypeID
                orderby sq.Order
                select q;

var questionsWithInclude = ((ObjectQuery)questions).Include("QuestionType");

foreach( var question in questionsWithInclude ) {
    Console.WriteLine("Question Type: " + question.QuestionType.Description);
}

(Ou utilisez la méthode d'extension mentionnée dans les articles.)

Si je comprends bien les articles liés, cela ne signifie pas nécessairement que cela fonctionnera maintenant (probablement pas), mais vous obtiendrez une exception vous donnant plus de détails sur le problème.

55
Slauma

Ajoutez "System.Data.Entity" et vous pourrez appeler Include sur IQueryable:

var questions = from q in db.Questions
                from sq in db.SurveyQuestions
                where sq.Survey == surveyTypeID
                orderby sq.Order
                select q;

questions = questions.Include("QuestionType");

voir: Comment puis-je convertir un DBQuery <T> en ObjectQuery <T>?

24
Akli

J'ai rencontré ce problème de Include(e => e.NavigationProperty) ne fonctionnant pas, mais la solution était différente de celle ci-dessus.

Le code problématique était le suivant:

    UserTopic existingUserTopic = _context.UserTopics
            .Include(ut => ut.Topic)
            .FirstOrDefault(t => t.UserId == currentUserId && t.TopicId == topicId);

        if (existingUserTopic != null)
        {
            var entry = _context.Entry(existingUserTopic);
            entry.State = EntityState.Deleted;

            if (existingUserTopic.Topic.UserCreated) 
            {
                var topicEntry = _context.Entry(existingUserTopic.Topic);
                entry.State = EntityState.Deleted;
            }

            await _context.SaveChangesAsync();
        }

Le problème était donc l'ordre du code. Entity Framework semble annuler les propriétés de navigation en mémoire dès qu'une entité est marquée comme EntityState.Deleted. Donc, pour accéder à existingUserTopic.Topic dans mon code, je dois le faire avant de marquer existingUserTopic supprimé.

5
parliament

Comme cette question est le premier résultat de recherche pour "Entity Framework Inclure ne fonctionne pas", je vais juste mentionner quelques autres possibilités, même si aucune n'est pertinente pour le message original de @ Dismissile.

Sensibilité à la casse

SQL Server (et éventuellement d'autres plates-formes de bases de données) fonctionnent souvent de manière non sensible à la casse. Donc, si vous avez une valeur de clé primaire ABC1, la base de données acceptera ABC1, abc1, AbC1, etc. comme valeurs de clé étrangère valides. Cependant, les comparaisons de chaînes .Net sont sensibles à la casse par défaut, donc même si votre .Include génère le SQL supplémentaire pour extraire les valeurs supplémentaires dans EF, il peut ne pas remplir les objets enfants s'il y a une différence de casse dans les clés. Ceci est discuté un peu plus en profondeur dans cette question SO avec quelques bons liens. L'utilisation d'un classement sensible à la casse pour les colonnes de clé primaire et de clé étrangère peut réduire le risque de cette cause d'échec .Include.

Espaces de fin

C'est celui qui m'a fait perdre un jour de ma vie à essayer de comprendre pourquoi mon .Include ne fonctionnait pas. SQL Server (et éventuellement d'autres plates-formes de base de données) ignorent souvent les espaces de fin dans les comparaisons de chaînes. Donc, si vous avez une valeur de clé primaire (sans les guillemets) "ABC" (un espace de fin), la base de données acceptera "ABC" (un espace), "ABC" (pas d'espace), "ABC" (2 espaces ) etc. en tant que valeurs de clé étrangère valides. Cependant, les comparaisons de chaînes .Net n'ignorent pas les espaces de fin, donc même si votre .Include génère le SQL supplémentaire pour extraire les valeurs supplémentaires dans EF, il peut ne pas remplir les objets enfants s'il y a des différences d'espaces de fin dans les clés. Le comportement de SQL Server est décrit dans cette page MS Support . Je n'ai pas élaboré de bonne stratégie pour empêcher ce type de .Incluez les échecs autres qu'une gestion prudente des données, c'est-à-dire ne laissez pas les utilisateurs saisir des valeurs de clé étrangère - utilisez une liste déroulante ou une entrée utilisateur rtrim religieusement.

0
Rhys Jones