2017-10-13 15 views
2

J'optimise du code que nous utilisons pour interroger Active Directory. L'une des méthodes récupère tous les utilisateurs AD qui ont changé depuis une mise à jour particulière, déterminée par la propriété uSNCreated de l'entrée d'annuaire. Essentiellement, il est fait le C# équivalent de:Obtenir la méthode Invokable à partir de __ComObject

select * from PrincipalSearcher où uSNCreated> somevalue

Le code est (plus ou moins):

public IEnumerable<UserPrincipal> GetUpdatedUsers(string samAccountName, long lastUsnChanged) 
{ 
    using (var context = new PrincipalContext(ContextType.Domain)) 
    using (var userSearcher = new PrincipalSearcher(new UserPrincipal(context))) 
    { 
     var items = userSearcher.FindAll().Cast<UserPrincipal>(); 
     return items.Where(x => GetUsnChanged(x) > lastUsnChanged).ToArray(); 
    } 
} 

private static long GetUsnChanged(Principal item) 
{ 
    var de = item.GetUnderlyingObject() as DirectoryEntry; 
    if (de == null) 
     return 0; 

    if (!de.Properties.Contains("uSNCreated")) 
     return 0; 

    var usn = de.Properties["uSNCreated"].Value; 
    var t = usn.GetType(); 

    var highPart = (int)t.InvokeMember("HighPart", BindingFlags.GetProperty, null, usn, null); 
    var lowPart = (int)t.InvokeMember("LowPart", BindingFlags.GetProperty, null, usn, null); 

    return highPart * ((long)uint.MaxValue + 1) + lowPart; 
} 

Maintenant, ce code fonctionne, mais les appels répétés à InvokeMember() sont SLOW. Ce que je voudrais faire est d'obtenir une référence aux propriétés HighPart et LowPart afin que je puisse les appeler encore et encore sans avoir à les redécouvrir chaque fois que j'appellerai InvokeMember().

je bien que je pouvais faire quelque chose le long des lignes de

static PropertyInfo highProp = highProp 
    ?? t.GetProperty("HighPart", BindingFlags.GetProperty); 
highPart = (int)highProp.GetValue(usn); 

Unfortnately t.GetProperty() retourne toujours null. En regardant les résultats retournés par GetProperties(), GetMethods() et GetMembers(), il ne semble pas y avoir un "HighPart" ou "LowPart" visible que je peux atteindre, même en utilisant BindingFlags.NonPublic - le __ComObject simplement ne semble pas les exposer (même si je peux appeler l'utilisation InvokeMember())

Existe-t-il un moyen de résoudre cela, ou est-il temps d'admettre la défaite?

+1

Vous pouvez copier/coller la [déclaration d'interface] (https://referencesource.microsoft.com/#System.Web/Security/ADMembershipProvider.cs,96419c0a980e9d0e). Ou ajoutez une référence à c: \ windows \ system32 \ activeds.tlb. Convertissez la valeur de retour de la propriété Value en cette interface. Les chances que cela est réellement plus rapide ne sont pas si élevés, vous êtes sûrement juste de voir le coût du réseau aller-retour au contrôleur de domaine. –

+0

Vous avez raison, cela ne semble pas faire quoi que ce soit pour la performance, mais cela rend le code plus lisible. Cela vaut un +1 dans mon livre - merci! – Pete

Répondre

2

Les classes de l'espace de noms System.DirectoryServices.AccountManagement sont conçues pour être utilisées dans des cas simples, par exemple. g. vous devez trouver un utilisateur ou un groupe. Ces classes ont des problèmes de performance connus. Je recommande d'utiliser DirectorySearcher ou LdapConnection/SearchRequest. Dans ce cas, vous pouvez filtrer les objets sur le serveur, pas sur le client, ce qui augmentera considérablement les performances et réduira les données envoyées sur le réseau. Voici un exemple d'utilisation de DirectorySearcher pour trouver tous les utilisateurs: Get all users from AD domain Dans votre cas, le filtre ressemblera (& (objectClass = utilisateur) (uSNCreated> = x + 1)) où x est votre dernier usn. Sachez que si vous effectuez le suivi des objets avec l'attribut usnCreated, vous obtiendrez uniquement les utilisateurs créés depuis la dernière utilisation. Pour suivre les modifications, utilisez l'attribut usnChanged.

+0

Bon appel sur le uSNCreated vs uSNChanged. J'ai essayé d'utiliser un DirectorySeacher mais continuez à obtenir une exception ArgumentException en se plaignant que "Le filtre de recherche (& (& (objectClass = utilisateur) (uSNChanged> 132440887))) n'est pas valide.". Je vais continuer à brancher avec cela - je préfère de loin l'idée de filtrage côté serveur des résultats – Pete

+0

Remplacer> avec> = dans le filtre et augmenter la dernière usn par un – oldovets

+0

Je suis absolument gobsmacked! DirectorySearcher ne peut pas gérer un opérateur> ??? C'est incroyable, mais tu as raison. Cela fonctionne comme un charme - merci! – Pete