2010-03-01 3 views
4

J'essaye d'écrire un utilitaire pour voir si un utilisateur s'est connecté à Windows depuis une date que j'ai stockée dans une base de données.Recherche rapide des résultats dans un objet Enumerable

private void bwFindDates_DoWork(object sender, DoWorkEventArgs e) 
{ 
    UserPrincipal u = new UserPrincipal(context); 
    u.SamAccountName = "WebLogin*"; 
    PrincipalSearcher ps = new PrincipalSearcher(u); 
    var result = ps.FindAll(); 
    foreach (WebAccess.WebLoginUsersRow usr in webAccess.WebLoginUsers) 
    { 
     UserPrincipal b = (UserPrincipal)result. 
      Single((a) => a.SamAccountName == usr.WEBUSER); 
     if (b.LastLogon.HasValue) 
     { 
      if (b.LastLogon.Value < usr.MODIFYDATE) 
       usr.LastLogin = "Never"; 
      else 
       usr.LastLogin = b.LastLogon.Value.ToShortDateString(); 
     } 
     else 
     { 
      usr.LastLogin = "Never"; 
     } 
    } 
} 

Cependant, les performances sont très lentes. La liste d'utilisateurs que je tire a environ 150 utilisateurs de Windows, donc quand je tape la ligne UserPrincipal b = (UserPrincipal)result.Single((a) => a.SamAccountName == usr.CONVUSER); il faut 10 à 15 secondes pour qu'il se termine par utilisateur (en passant par je peux voir qu'il fait l'étape a.SamAccountName == usr.CONVUSE est exécuté pour chaque personne si le pire des cas est O (n^2) fois)

Des recommandations sur les façons d'améliorer mon efficacité?

+2

Je déteste la façon dont SO aime parfois traiter les onglets dans une section de code et parfois non. –

Répondre

3

Il est surprenant que Single() prenne autant de temps sur une si petite liste. Je dois croire que quelque chose d'autre se passe ici. L'appel à ps.FindAll() peut renvoyer un objet qui ne cache pas les résultats et vous oblige à passer un appel coûteux à une ressource à chaque itération au sein de Single().

Vous pouvez utiliser un profileur pour déterminer l'heure à laquelle vous appuyez sur cette ligne. Je suggère également de regarder la mise en œuvre de FIndAll() parce qu'il retourne quelque chose d'inhabituellement coûteux à parcourir. Donc, après avoir lu votre code de plus près, il est logique pourquoi Single() est si cher. La classe PrincipalSearcher utilise le magasin de services de répertoire comme référentiel à partir duquel effectuer la recherche. Il ne cache pas ces résultats. C'est ce qui affecte votre performance.

Vous souhaitez probablement matérialiser la liste en utilisant ToList() ou ToDictionary() afin que l'accès aux informations principales se fasse localement.

Vous pouvez également éviter complètement ce type de code et utiliser la méthode FindOne() à la place, ce qui vous permet d'interroger directement le principal que vous voulez.

Mais si vous ne pouvez pas utiliser cela, alors quelque chose comme cela devrait fonctionner mieux:

result.ToDictionary(u => u.SamAccountName)[usr.WEBUSER] 
4

je suggère:

var result = ps.FindAll().ToList(); 

Depuis PrincipalSearchResult ne cache pas comme d'autres choses, ce vous amènera près d'un niveau de performance O (n).

3
var userMap = result.ToDictionary(u => u.SamAccountName); 

foreach (WebAccess.WebLoginUsersRow usr in webAccess.WebLoginUsers) 
{ 
    UserPrincipal b = userMap[usr.WEBUSER]; 

    // ... 
} 
+0

J'aime celui-ci. +1 – Randolpho

+0

Notez que j'ai utilisé 'ToDictionary' car votre utilisation de' Single' implique que vous attendez exactement une correspondance pour chaque 'WebLoginUsersRow'. Ajuster si nécessaire. – mquander

+0

Merci. environ 15 secondes pour la première entrée et instantanée pour le reste. –

Questions connexes