2

Nous obtenons l'exception de référence null classique, "Référence d'objet non définie sur une instance d'un objet." Rien d'inhabituel ici, sauf qu'il se passe sur un IdentityUser qui vient directement du contexte.Inconsistenty "Référence d'objet non définie sur une instance d'un objet" sur la clé primaire de AspNetUsers

La ligne de déclenchement est l'exception:

allUsers.FirstOrDefault(u => u.Id == log.ActionByUserId) 

et l'exception est déclenchée à u.Id. Comment pourriez-vous jamais être nul dans ce scénario? Cela me suggère que l'une des entrées dans allUsers est null, mais je ne comprends pas comment cela se produirait lorsque allUsers est extrait de la base de données par Entity Framework. Cela se produit sur une action de contrôleur en lecture seule, donc nous n'avons pas essayé de créer un utilisateur plus tôt dans la requête.

Cette erreur se produit par intermittence sur notre instance de production, mais ne se produit pas lorsque nous restaurons la même base de données dans un environnement de développement.

code plus pour le contexte (bien que j'ai simplifié ce code aux sections pertinentes aussi):

Contexte (la classe ApplicationUser a son propre contexte, séparé du contexte partagé par la plupart de nos autres entités):

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 
{ 
    public ApplicationDbContext() 
     : base("DefaultConnection") 
    { 
    } 

    public static ApplicationDbContext Create() 
    { 
     return new ApplicationDbContext(); 
    } 
} 

service:

public class UserService 
{ 
    private readonly ApplicationDbContext _context; 

    public IEnumerable<ApplicationUser> GetUsers() 
    { 
     var contextUsers = _context.Users; 
     return contextUsers; 
    } 
} 

objet domaine

public class ApplicationUser : IdentityUser 
{ 
    // Id is inherited from IdentityUser 
} 

Action de contrôleur:

public ActionResult ParticipantLog(int participantId) 
{ 
    var allUsers = UserService.GetUsers(); 
    var log = LogService.GetParticipantLog(participantId); 
    var model = ParticipantLogsModelMapper.Create(log, allUsers); 
} 

Mapper:

public static ParticipantLogsViewModel Create(ParticipantLog log, IEnumerable<ApplicationUser> allUsers) 
{ 
    var actionBy = allUsers.FirstOrDefault(u => u.Id == log.ActionByUserId); 
    var model = new ParticipantLogViewModel 
    { 
     ActionBy = actionBy.DisplayName 
    }; 

    return model; 
} 
+0

Pourquoi vous pouvez être sûr du numéro de ligne, même quelle variable qui est nulle? Êtes-vous capable d'attacher le débogueur au serveur de production? –

+0

@ThariqNugrohotomo C'est une application interne. Nous renvoyons des messages d'erreur complets lorsqu'il existe une exception. – thelem

+1

En supposant que cette ligne est le coupable réel, je soupçonne d'abord les variables 'log' et' allUsers'. Il existe cependant un autre problème possible, par ex. problème de thread-sécurité - depuis le modèle multitâche de Web ASP. –

Répondre

2

De:

allUsers.FirstOrDefault(u => u.Id == log.ActionByUserId) 

Si allUsers ne contient pas d'enregistrements, puis default(T) est retourné, qui dans ce L'instance sera null.

J'ai mis en place une application console minimale (tirant une référence à Microsoft.AspNet.Identity.EntityFramework.

static void Main(string[] args) 
{ 
    var p = default(ApplicationUser); 
} 

public class ApplicationUser : IdentityUser 
{ 
    // Id is inherited from IdentityUser 
} 

Et p est en effet nulle La méthode d'extension FirstOrDefault, par sa nature même, va soit retourner le premier élément dans le IEnumerable ou la valeur par défaut pour le <T> s'il n'y a rien dans la liste, ce qui semble être ce qui se passe dans ce cas.

Cette erreur se produit par intermittence sur notre instance de production, mais ne se produit pas lorsque nous restaurons la même base de données dans un environnement de développement.

Si vous avez simplifié le code que vous avez montré, avez-vous elided une vérification des erreurs/manipulation que ça entraîne une a échoué tentative de récupération de données d'utilisateur à partir de la base de données renvoyant un ensemble vide? C'est une façon de voir le scénario qui se passe ici.

Pour aider à réduire cette baisse, la production, vous pouvez mettre une ligne d'enregistrement des diagnostics au-dessus de l'appel allUsers.FirstOrDefault qui vérifie allUsers.Any() pour confirmer qu'il ya des éléments présents dans allUsers et puis essayer de lier ce retour à tout autre diagnostic que vous avez . Le problème avec les environnements de développement est, alors qu'ils ont rarement autant de production, ils ont généralement une quantité minime de charge, en comparaison. Il en résulte des problèmes qui se produisent en raison de l'exploitation en charge tout simplement pas être ramassé = (

+0

Ce n'est pas la nullité qui est mon problème, c'est toi. J'obtiens l'erreur de référence nulle * dans * le FirstOrDefault. Je suis OK avec p (actionBy) étant nulle si elle ne peut pas trouver un utilisateur avec l'ID donné. – thelem

+0

@Rob Aucune vérification d'erreur supprimée. _context.Users est rempli par EF et transmis non modifié jusqu'à ce que la ligne lève l'exception. – thelem

+0

@thelem, mea culpa, j'ai confondu quelques trucs et je me suis retrouvé avec une petite réponse - la seule autre chose que je peux penser/suggérer est que allUsers * doit * avoir un ApplicationUser nul en lui. Comment votre application est-elle composée? Y a-t-il une possibilité que plusieurs demandes partagent le même ApplicationDbContext et qu'une certaine comptabilité "sous le capot" se brise, par ex. un utilisateur a été supprimé sur une autre demande qui a invalidé cet utilisateur particulier et cela est reflété dans votre IEnumerable ? – Rob

0

En établissant le profil du serveur de base de données, j'ai remarqué que la ligne de problème.

allUsers.FirstOrDefault(u => u.Id == log.ActionByUserId) 

est dénombraient toute la table derrière AllUsers I Je pensais que c'était parce que allUsers sont des objets IdentityUser, plutôt que des objets de domaine EF standard, et que u.Id est de type TKey plutôt qu'une primitive standard:

Ma solution est intead de passer dans un IEnumerable je passe dans mon UserService, qui comprend une méthode pour appeler findById sur UserManager

class UserService 
{ 
    private readonly UserManager<ApplicationUser> _userManager; 

    public UserService(ApplicationDbContext context) 
    { 
     _userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context)); 
    } 

    public ApplicationUser Find(string userId) 
    { 
     return _userManager.FindById(userId); 
    } 
}