12

Active Directory を介して再帰的にユーザーの直属の部下をすべて取得しようとしています。したがって、ユーザーが与えられると、この人をマネージャーとして持つすべてのユーザー、またはマネージャーとしての人を持つマネージャーとしての人を持つすべてのユーザーのリストになります...最終的には入力ユーザーをマネージャーとして持ちます。

私の現在の試みはかなり遅いです:

private static Collection<string> GetDirectReportsInternal(string userDN, out long elapsedTime)
{
    Collection<string> result = new Collection<string>();
    Collection<string> reports = new Collection<string>();

    Stopwatch sw = new Stopwatch();
    sw.Start();

    long allSubElapsed = 0;
    string principalname = string.Empty;

    using (DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("LDAP://{0}",userDN)))
    {
        using (DirectorySearcher ds = new DirectorySearcher(directoryEntry))
        {
            ds.SearchScope = SearchScope.Subtree;
            ds.PropertiesToLoad.Clear();
            ds.PropertiesToLoad.Add("directReports");
            ds.PropertiesToLoad.Add("userPrincipalName");
            ds.PageSize = 10;
            ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
            SearchResult sr = ds.FindOne();
            if (sr != null)
            {
                principalname = (string)sr.Properties["userPrincipalName"][0];
                foreach (string s in sr.Properties["directReports"])
                {
                    reports.Add(s);
                }
            }
        }
    }

    if (!string.IsNullOrEmpty(principalname))
    {
        result.Add(principalname);
    }

    foreach (string s in reports)
    {
        long subElapsed = 0;
        Collection<string> subResult = GetDirectReportsInternal(s, out subElapsed);
        allSubElapsed += subElapsed;

        foreach (string s2 in subResult)
        {
        result.Add(s2);
        }
    }



    sw.Stop();
    elapsedTime = sw.ElapsedMilliseconds + allSubElapsed;
    return result;
}

基本的に、この関数は入力として識別名 (CN=Michael Stum、OU=test、DC=sub、DC=domain、DC=com) を受け取ります。これにより、ds.FindOne() の呼び出しが遅くなります。

userPrincipalName を検索する方がはるかに高速であることがわかりました。私の問題: sr.Properties["directReports"] は単なる文字列のリストであり、それが識別名であり、検索が遅いようです。

私は、distinguishedName と userPrincipalName の間で変換する高速な方法はあるのだろうか? または、作業するのが識別名しかない場合、ユーザーを検索するより高速な方法はありますか?

編集:答えてくれてありがとう!Manager-Field を検索すると、機能が 90 秒から 4 秒に改善されました。これは新しく改善されたコードで、より高速で読みやすくなっています (経過時間機能にバグがある可能性が最も高いことに注意してください。ただし、関数の実際のコアは機能します)。

private static Collection<string> GetDirectReportsInternal(string ldapBase, string userDN, out long elapsedTime)
{
    Collection<string> result = new Collection<string>();

    Stopwatch sw = new Stopwatch();
    sw.Start();
    string principalname = string.Empty;

    using (DirectoryEntry directoryEntry = new DirectoryEntry(ldapBase))
    {
        using (DirectorySearcher ds = new DirectorySearcher(directoryEntry))
        {
            ds.SearchScope = SearchScope.Subtree;
            ds.PropertiesToLoad.Clear();
            ds.PropertiesToLoad.Add("userPrincipalName");
            ds.PropertiesToLoad.Add("distinguishedName");
            ds.PageSize = 10;
            ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
            ds.Filter = string.Format("(&(objectCategory=user)(manager={0}))",userDN);

            using (SearchResultCollection src = ds.FindAll())
            {
                Collection<string> tmp = null;
                long subElapsed = 0;
                foreach (SearchResult sr in src)
                {
                    result.Add((string)sr.Properties["userPrincipalName"][0]);
                    tmp = GetDirectReportsInternal(ldapBase, (string)sr.Properties["distinguishedName"][0], out subElapsed);
                    foreach (string s in tmp)
                    {
                    result.Add(s);
                    }
                }
            }
          }
        }
    sw.Stop();
    elapsedTime = sw.ElapsedMilliseconds;
    return result;
}
4

1 に答える 1

10

まず、探している DN が既にある場合は、スコープを「サブツリー」に設定する必要はありません。

また、「マネージャー」プロパティが探している人物であるすべてのオブジェクトを見つけて、それらを繰り返します。これは通常、その逆よりも高速です。

(&(objectCategory=user)(manager=<user-dn-here>))

編集:以下は重要ですが、これまでのところ、この回答へのコメントでのみ言及されています:

上記のようにフィルター文字列を作成すると、DN としては有効であるが、フィルターでは特別な意味を持つ文字で文字列が壊れる危険性があります。これらはエスケープする必要があります:

*   as  \2a
(   as  \28
)   as  \29
\   as  \5c
NUL as  \00
/   as  \2f

// Arbitrary binary data can be represented using the same scheme.

編集: SearchRootto をオブジェクトの DN に設定すると、SearchScopetoBaseも AD から単一のオブジェクトをプルする高速な方法になります。

于 2008-10-10T08:51:38.000 に答える