Получить вызываемый метод из __ComObject

Я оптимизирую некоторый код, который мы используем для запроса Active Directory. Один из методов извлекает всех пользователей AD, которые изменились с момента определенного обновления, что определяется свойством uSNCreated записи каталога. По сути, это эквивалент С#:

выберите * из PrincipalSearcher, где uSNCreated > какое-то значение

Код (более или менее):

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;
}

Теперь этот код работает, но повторные вызовы InvokeMember() выполняются МЕДЛЕННО. Что я хотел бы сделать, так это получить ссылку на свойства HighPart и LowPart, чтобы я мог вызывать их снова и снова без накладных расходов, связанных с необходимостью «повторно открывать» их каждый раз при вызове InvokeMember().

Я бы хотел, чтобы я мог сделать что-то вроде

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

К сожалению, t.GetProperty() всегда возвращает значение null. Глядя на результаты, возвращаемые GetProperties(), GetMethods() и GetMembers(), кажется, что нет видимых "HighPart" или "LowPart", к которым я могу получить доступ, даже при использовании BindingFlags.NonPublic - просто __ComObject кажется, не раскрывает их (хотя я могу вызвать использование InvokeMember())

Есть ли способ решить эту проблему, или пора признать поражение?


person Pete    schedule 13.10.2017    source источник
comment
Вы можете скопировать/вставить декларацию интерфейса. Или добавьте ссылку на c:\windows\system32\activeds.tlb. Приведите возвращаемое значение свойства Value к этому интерфейсу. Вероятность того, что это на самом деле быстрее, не так высока, вы наверняка просто видите стоимость сетевого пути к контроллеру домена.   -  person Hans Passant    schedule 15.10.2017
comment
Вы правы в том, что это никак не влияет на производительность, но определенно делает код более читабельным. Это стоит +1 в моей книге - спасибо!   -  person Pete    schedule 16.10.2017


Ответы (1)


Классы из пространства имен System.DirectoryServices.AccountManagement предназначены для использования в простых случаях, например. г. вам нужно найти пользователя или группу. У этих классов есть известные проблемы с производительностью. Я бы рекомендовал использовать DirectorySearcher или LdapConnection/SearchRequest. В этом случае вы можете фильтровать объекты на сервере, а не на клиенте, что значительно повысит производительность и уменьшит количество данных, отправляемых по сети. Вот пример использования DirectorySearcher для поиска всех пользователей: Получить всех пользователей из домена AD В вашем случае фильтр будет выглядеть так (&(objectClass=user)(uSNCreated>=x+1)) где x ваш последний usn. Имейте в виду, что если вы отслеживаете объекты с атрибутом usnCreated, вы будете получать только тех пользователей, которые были созданы с момента последнего usn. Для отслеживания изменений используйте атрибут usnChanged

person oldovets    schedule 15.10.2017
comment
Хороший вызов для uSNCreated против uSNChanged. Я пытался использовать DirectorySeacher, но продолжаю получать исключение ArgumentException, жалуясь на то, что поисковый фильтр (&(&(objectClass=user)(uSNChanged › 132440887))) недействителен. предпочитаю идею фильтрации результатов на стороне сервера - person Pete; 16.10.2017
comment
Замените › на ›= в фильтре и увеличьте последний usn на единицу - person oldovets; 16.10.2017
comment
Я совершенно ошеломлен! DirectorySearcher не может обработать оператор › ??? Это удивительно, но ты прав. Это работает как шарм - спасибо! - person Pete; 16.10.2017