2017-02-05 4 views
3

Active Directoryを照会し、表示名がフォーマット{displayName}のすべてのユーザーとグループを検索するC#でメソッドを作成しています(先頭と末尾にワイルドカード)、このメソッドはオートコンプリートフィールドに使用されます。ワイルドカードを使用したActive Directoryクエリのパフォーマンスが低い

問題は、私が書いたメソッドのパフォーマンスが非常に悪く、クエリの文字列に応じて30秒から1分の間に何かを取ります。

私の組織のADは非常に大きいですが、この時間がかかる場合、オートコンプリートフィールドは無意味です。ここで

私が今使っているコードです:

// Intialize the results list. 
result.queryResult = new List<Classses.ADSearchObject>(); 

// Set up domain context. 
PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain, Constants.adQueryUser, Constants.adQueryPassword); 

// Set up a directory searcher. 
DirectorySearcher dSearcher = new DirectorySearcher(); 
// Define a SearchCollection to store the results. 
SearchResultsCollection searchCol; 
// Define returned result paging for performance. 
dSearcher.PageSize = 1000; 
// Define the properties to retrieve 
dSearcher.PropertiesToLoad.Add("sAMAccountName"); 
dSearcher.PropertiesToLoad.Add("displayName"); 
// Define the filter for users. 
dSearcher.Filter = $"(|(&(displayName = {result.querystring}*)(objectCategory=person))(&(displayName=*{result.querystring})(objectCategory=person)))"; 

// Search based in filter and save the results. 
searchCol = dSearcher.FindAll(); 

// Add the results to the returned object 
foreach (SearchResult searchResult in searchCol) 
{ 
    DirectoryEntry de = searchResult.GetDirectoryEntry(); 
    // Code to get data from the results... 
} 

// Define the filter for groups. 
dSearcher.Filter = $"(|(&(displayName={result.querystring}*)(objectCategory=person))(&(displayName=*{result.querystring})(objectCategory=person)))"; 

// Search based in filter and save the results. 
searchCol = dSearcher.FindAll(); 

// Add the results to the returned object 
foreach (SearchResult searchResult in searchCol) 
{ 
    DirectoryEntry de = searchResult.GetDirectoryEntry(); 
    // Code to get data from the results... 
} 

現在の検索はそれらを区別することが簡単にするために、ユーザーとグループに分かれていますが、それは、実質的にパフォーマンスを向上させた場合、私はそれらを統一します単一の検索。

編集:ユーザールネが示唆したように、私はFindAllのにかかる時間を確認するためにStopwatchを使用して、私はまた私のforeachループが取るどのくらい確認。

FindAllコールは、ADによって索引付けされた先頭のワイルドカード(検索されない)で検索しても、約100ms(非常に高速)であることがわかりました。

明らかに、最も長くかかるコールは、私のforeachループで約40秒(40,000ms)かかります。

私はその性能を改善する方法を考え出したていないように私は私のforeachループ内のコードブロックで質問を更新しています:私は私の更新で私の「ストップウォッチ」を開始し、停止

// --- I started a stopwatch here 
foreach (SearchResult searchResult in searchCol) 
{ 
    // --- I stopped the stopwatch here and noticed it takes about 30,000ms 
    result.code = 0; 

    DirectoryEntry de = searchResult.GetDirectoryEntry(); 

    ADSearchObject adObj = new ADSearchObject(); 

    adObj.code = 0; 

    if (de.Properties.Contains("displayName") 
    { 
     adObj.displayName = de.Properties["displayName"].Value.ToString(); 
    } 

    adObj.type = "user"; 

    result.queryResults.Add(adObj); 
} 

注意をコード、私はなぜループを開始するのがそんなに長いかわからない。

+0

これはMicrosoftのADを扱うときの動作です。たぶん、ADツリーに何らかの種類のDBを同期させることで、毎回ADを照会するのではなく、ミリ秒単位でクエリを実行できるようになるかもしれません。 – r1verside

+2

'(&(displayName = * {result.querystring} *)(objectCategory = person))'はあなたのフィルタと同じですか?そして 'FindAll'呼び出しをStopWatchできますか?それらは最も時間を取っていますか? – rene

+0

@rene投稿に編集したものを見て、 'Stopwatch'から取り出した時間を追加しました。 –

答えて

1

もちろん、部分文字列の一致は、一意の値の等値一致よりもコストがかかります。また、あなたのプロファイリングに応じて、全体の40秒を消費するイテレータブロックには、ライオンの経過時間の分け目が含まれています。

イテレータを設定するだけでパフォーマンスが大幅に低下すると確信しているのなら、私はそうではありません。これは、タイミングポイントを選択したためです。

StartClock("foreach"); 
foreach (SearchResult searchResult in searchCol) 
{ 
    // use an empty block to speed things up or 
    StopClock("foreach"); 
    // whatever 
    RestartClock("foreach"); 
} 
StopClock("foreach"); 
LogClock("foreach"); 

私はあなたが私はすでにコメントし、ベストプラクティスに注意を払っているのであれば(大エントリ番号の)巨大なパフォーマンスの向上を期待する:検索結果に必要なすべてが供給サーバーへの単一のリクエストを送信し、ドン各項目に対して別のリクエストを送信しないでください。 GetDirectoryEntry()への1回の呼び出しでは、が1msしか消費されませんが、エントリの数が多いため、アプリケーションの自動補完機能でコードが役に立たなくなります。

そのフィルタ表現のための通常の形式を提示するための@reneへの功績。 Active Directoryのフィルタ最適化についてはわかりませんので、私は確かな道を踏みます

(&(objectCategory=person)(displayName=*{result.querystring}*)) 
関連する問題