首页 > 解决方案 > C# 目录服务很快,但读取结果真的很慢

问题描述

我正在为我的 Active Directory 中的所有组运行以下代码。

我要做的是首先运行查询以获取我的 Active Directory 中的所有组并返回它们的专有名称。之后我使用下面的代码来获取每个组中所有子组的列表。这包括嵌套的子组。该代码正在运行,尽管它真的很慢。

LDAP 查询总是执行得非常快,但是当我尝试处理结果时,它却非常慢。有什么我可以用来加快这个过程的吗?

目前我需要大约 2 小时来执行大约 20000 个看起来像的组。(我的广告中有超过 200 万)。

以下是我使用的代码:

获取所有组

public override IEnumerable<ADGroupObj> GetCustomGroupAndMember(ADSetting setting)
{
    var m_strADFilter = configHandler.GetCustomGroupFilterString().ToString();
    string classAndMethodName = $"{this.GetType().Name}.{MethodBase.GetCurrentMethod().Name}";
    string[] properties = { "objectGUID", "cn", "distinguishedName", "member", "objectSid" };

    //group
    m_log.DebugFormat("[{0}]: [GetAllGroupAndMemberFromDomainController] m_strADFilter = {1}", classAndMethodName, m_strADFilter);
    if (string.IsNullOrWhiteSpace(m_strADFilter))
    {
        string strErrorMsg = string.Format("[{0}]: Incorrect AD filter string !!!", classAndMethodName);
        m_log.ErrorFormat(strErrorMsg);
        throw new Exception(strErrorMsg);
    }

    bool isSuccess = false;

    using (ADServerWrapper instADServer = ADServerWrapper.GetADServerWrapper(setting, this.ConnectType))
    {
        int iTotalCount = 0;
        int iInvalidCount = 0;
        int iValidCount = 0;

        var results = instADServer.QueryADbyFilterObj(m_strADFilter, properties);
        if (results == null)
        { throw new Exception("Error occured while invoking instADServer.QueryADbyFilterObj."); }

        (var whiteListDomainOUs, var blackListDomainOUs) = configHandler.GetADSpecifyDomainOU();
        foreach (SearchResult result in results)
        {
            ++iTotalCount;

            // Check Result valid or not
            ADGroupObj adGroup = AnalyzeADGroupObj(result.Properties, whiteListDomainOUs, blackListDomainOUs);
            adGroup = GetAllGrpMembers(adGroup,setting);
            if (adGroup != null && ValidateADGroup(adGroup))
            {
                ++iValidCount;
                configHandler.UpdateCustomGroupDN(adGroup.DN);
                yield return adGroup;
                m_log.DebugFormat("[{0}]: AD Group ([Valid]/[total] count=[{1}]/[{2}]): {3}", classAndMethodName, iValidCount, iTotalCount, adGroup.Name);
            }
            else
            {
                ++iInvalidCount;
                m_log.DebugFormat("[{0}]: AD Group ([Null or inValid]/[total] count={1}/{2})", classAndMethodName, iInvalidCount, iTotalCount);
            }
        }

        isSuccess = true;
    }

    if (!isSuccess)
    {
        string strErrorMsg = string.Format("[{0}]: Did not complete successfully, isSuccess == false.", classAndMethodName);
        m_log.ErrorFormat(strErrorMsg);
        throw new Exception(strErrorMsg);
    }
}

获取所有子组

protected ADGroupObj GetAllGrpMembers(ADGroupObj aDGroup, ADSetting setting) {
    string methodName = MethodBase.GetCurrentMethod().Name;
    var forUserConnectType = ADServiceConnectType.AD_CONNECT_TYPE_GC | (this.connectType & ADServiceConnectType.AD_CONNECT_TYPE_SSL);
    var instADServer = ADServerWrapper.GetADServerWrapper(setting, forUserConnectType);
    string m_strADFilter = $"(&(objectCategory=group)(memberOf:1.2.840.113556.1.4.1941:={aDGroup.DN}))";
    string[] properties = {
            "distinguishedName"
        };
    try
    {
        aDGroup.Members = new List<string>();
        IEnumerable<SearchResult> results = instADServer.QueryADbyFilterObj(m_strADFilter, properties);
        if (results != null)
        {

            foreach (SearchResult result in results)
            {
               if (result.Properties["distinguishedName"] != null && result.Properties["distinguishedName"].Count > 0)
                {
                    var user = result.Properties["distinguishedName"][0];
                    aDGroup.Members.Add(user.ToString());
                }
            }
        }
        else
            m_log.DebugFormat("[{0}]:No value with member attribute!", methodName);
    }
    catch (Exception e)
    {


    }
    return aDGroup;
}

在这里,两者中的 foreach 结果列是最耗时的。不是 Query AD By Filter Obj。

供参考: QueruADByFilterObj

public IEnumerable<SearchResult> QueryADbyFilterObj(string strFilter, string[] properties)
{
    using (var searcher = new DirectorySearcher(m_ADEntryObject))
    {
        searcher.Filter = strFilter;
        searcher.SizeLimit = int.MaxValue;
        searcher.PageSize = int.MaxValue;
        searcher.CacheResults = false;
        searcher.PropertiesToLoad.AddRange(properties);
        //searcher.ReferralChasing = ReferralChasingOption.All;

        using (SearchResultCollection searchResults = searcher.FindAll())
        {
            foreach (SearchResult result in searchResults)
            {
                yield return result;
            }
        }
    }
}

标签: c#active-directoryldap

解决方案


你在做什么需要很长时间。您正在提取大量数据。

我写了一篇关于在使用 AD 编程时获得更好性能的文章,但似乎您已经在执行我在搜索时建议的操作(确保使用PropertiesToLoad)。但它看起来仍然有一些你没有在这里共享的代码,所以你可以查看它,看看是否还有其他可以调整的地方。

读取结果需要很长时间,因为结果以页面形式出现。您设置PageSizeint.MaxValue,但 AD 一次不会返回超过 1000 个结果。所以结果将以 1000 页的形式发送给您。这意味着当您尝试处理结果 1001 时,它将暂停,转到 AD 并获取下一页结果。在 2001、3001 等处也是如此。

了解您为什么要首先提取所有这些数据也可能会有所帮助。当它已经在 AD 中时,将它全部存储在其他地方似乎很奇怪。但我知道你可能刚刚被告知要这样做......这发生在我身上。

作为旁注,您是否阅读过C# 中的字符串插值?它可以让你的string.Format台词更容易阅读。例如,这一行:

string strErrorMsg = string.Format("[{0}]: Incorrect AD filter string !!!", classAndMethodName);

可以简化为:

var strErrorMsg = $"[{classAndMethodName}]: Incorrect AD filter string !!!";

推荐阅读