Исключения памяти при очистке Active Directory - PullRequest
0 голосов
/ 07 января 2020

Я пытаюсь очистить все группы, членами которых является пользователь в Active Directory (включая группы, членами которых они являются косвенные), но я сталкиваюсь с ошибками исключения памяти.

Последнее сообщение об ошибке передано было:

Недостаточно памяти для продолжения выполнения программы

Код, который я запускаю, запускается в фоновом режиме по расписанию.

У меня есть просмотрел ВСЕ ТАК активные записи группы активных каталогов и перепробовал как можно больше итераций этого процесса. Этот будет (обычно) работать, если я не запускаю его по расписанию. Иногда это может взломать sh программу.

Наша база данных Active Directory достаточно велика.

Любая помощь, которую вы можете предложить с этим, была бы полезной. Я не возражаю против того, чтобы это заняло немного больше времени, скорость не слишком важна, если я могу заставить ее работать хорошо и правильно.

Вот что у меня есть:

        DataTable resultsTable = new DataTable();
        resultsTable.Columns.Add("EmailID");
        resultsTable.Columns.Add("UserID");
        resultsTable.Columns.Add("memberOf");
        resultsTable.Columns.Add("groupType");
        resultsTable.Columns.Add("record_date");

        try
        {
            string RecordDate = DateTime.Now.ToString(format: "dd/MM/yyyy", provider: CultureInfo.CreateSpecificCulture("en-GB"));

            string ou = "OU=Groups,OU=nonya,DC=my,DC=domain,DC=net";

            using (PrincipalContext context = new PrincipalContext(ContextType.Domain, "my.domain.net", ou))
            {
                using (GroupPrincipal GroupPrin = new GroupPrincipal(context))
                {
                    using (var searcher = new PrincipalSearcher(GroupPrin))
                    {
                        using (var results = searcher.FindAll())
                        {
                            foreach (var result in results)
                            {
                                DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
                                string GUID = de.Guid.ToString();
                                string testname1 = de.Name;
                                string testParentName1 = de.Parent.Name;
                                string MasterGroupname = testname1.Substring(3, (testname1.Length - 3));
                                string type = testParentName1.Substring(3, (testParentName1.Length - 3));

                                using (var group = GroupPrincipal.FindByIdentity(context, IdentityType.Guid, GUID))
                                {
                                    using (var users = group.GetMembers(true))
                                    {
                                        foreach (Principal user in users)
                                        {
                                            DataRow dr1 = resultsTable.NewRow();

                                            dr1["EmailID"] = user.UserPrincipalName;
                                            dr1["UserID"] = user.SamAccountName;
                                            dr1["memberOf"] = MasterGroupname;
                                            dr1["groupType"] = type;
                                            dr1["record_date"] = RecordDate;

                                            resultsTable.Rows.Add(dr1);
                                            user.Dispose();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

        //Table is uploaded to SQL database here, unless it crashes before reaching this point
        }
        catch (Exception E)
        {
            logger.Error($"ADGroupScrape error. System says: {E.Message}");
            return false;
        }
        finally
        {
            resultsTable.Dispose();
        }

Ответы [ 3 ]

1 голос
/ 07 января 2020

Размещение каждого объекта в конце l oop, вероятно, решит вашу проблему (result.Dispose()). У меня были проблемы с памятью с длинными циклами при работе с AD. Предполагая, что вы работаете с тысячами результатов, и вы выделяете память для каждого, и нет никакого перерыва между обработкой каждого результата, сборщик мусора не имеет возможности очистить вас.

Но вы также без необходимости возвращаетесь в AD, чтобы найти группу, которую вы уже нашли (GroupPrincipal.FindByIdentity). Это также увеличит потребление памяти, но также замедлит всю операцию. Вместо этого просто приведите result к GroupPrincipal, чтобы вы могли вызвать .GetMembers() для него.

Не связано, но полезно: если у вас есть несколько using блоков, вложенных таким образом, вы можете объединить их все в один блок. Это экономит ваши отступы.

Вот как будет выглядеть ваш код со всеми этими предложениями:

using (PrincipalContext context = new PrincipalContext(ContextType.Domain, "my.domain.net", ou))
using (GroupPrincipal GroupPrin = new GroupPrincipal(context))
using (var searcher = new PrincipalSearcher(GroupPrin))
using (var results = searcher.FindAll())
{
    foreach (GroupPrincipal result in results)
    {
        DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
        string GUID = de.Guid.ToString();
        string testname1 = de.Name;
        string testParentName1 = de.Parent.Name;
        string MasterGroupname = testname1.Substring(3, (testname1.Length - 3));
        string type = testParentName1.Substring(3, (testParentName1.Length - 3));

        using (var users = result.GetMembers(true))
        {
            foreach (Principal user in users)
            {
                DataRow dr1 = resultsTable.NewRow();

                dr1["EmailID"] = user.UserPrincipalName;
                dr1["UserID"] = user.SamAccountName;
                dr1["memberOf"] = MasterGroupname;
                dr1["groupType"] = type;
                dr1["record_date"] = RecordDate;

                user.Dispose();
            }
        }
        result.Dispose();
    }
}

Это, вероятно, будет работать, но, возможно, все еще будет намного быстрее. Использование Principal объектов (и всего пространства имен AccountManagement) является оберткой вокруг DirectoryEntry / DirectorySearcher, которая упрощает вам задачу, но за счет производительности. Использование DirectoryEntry / DirectorySearcher напрямую всегда быстрее.

Если вы хотите поэкспериментировать с этим, я написал пару статей, которые помогут:

0 голосов
/ 10 апреля 2020

Пару месяцев спустя, и я снова начал получать исключения из памяти. добавлена ​​нумерация страниц (что я на самом деле не знаю, что вы могли бы сделать), и, похоже, это решило проблему. Строка, которую я добавил, была ((DirectorySearcher)searcher.GetUnderlyingSearcher()).PageSize = 500; между объявлением поисковика и searcher.FindAll().

                    using (var searcher = new PrincipalSearcher(GroupPrin))
                    {
                        ((DirectorySearcher)searcher.GetUnderlyingSearcher()).PageSize = 500;
                        using (var results = searcher.FindAll())
                        {
0 голосов
/ 09 января 2020

Так что проблема, похоже, не в моем коде, но спасибо всем за предложения по его очистке. Я изменил Target Platform в Configuration Manager (Visual Studio) на X64 , так как изначально он был установлен на " Любой ЦП ", и теперь мой код успешно выполняется каждый раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...