Симон Робинсон - C# для профессионалов. Том II
Запись изменений в объекты также происходит только в кэше. Задание множества свойств не генерирует сетевого трафика. Метод DirectoryEntry.CommitChanges() требуется для очистки кэша и переноса всех измененных данных на сервер. Чтобы снова получить вновь записанные данные из хранилища каталога, можно для чтения свойств использовать метод DirectoryEntry.RefreshCache(). Задание свойства UsePropertyCache как false может быть очень полезно для отладки, чтобы увидеть, какое свойство было изменено неправильно.
Обновление записей каталога
Объекты в активном каталоге обновляются так же легко, как и читаются. Изменение значений возможно после считывания объекта. Чтобы удалить все значения одного свойства, может вызываться метод PropertyValueCollection.Clear(). С помощью метода Add() к свойству могут добавляться новые значения. Remove() и RemoveAt() удаляют специфические значения из коллекции свойства:
using (DirectoryEntry de = new DirectoryEntry!)) {
de.Path =
"LDAP://celticrain/CN=Christian Nagel, " +
"OU=Wrox Press, DC=eichkogelstrasse, DC=local";
if (de.Properties.Contains("mobile")) {
de.Properties["mobile"][0] = "+43 (664) 3434343434";
}
de.CommitChanges();
}
Чтобы изменить значение, зададим ему определенное значение. Посредством следующей строки кода для номера мобильного телефона задается новое значение, если оно существует, с использованием индекса PropertyValueCollection. С помощью индекса значение может только изменяться. Поэтому необходимо всегда проверять методом DirectoryEntry.Properties.Contains(), доступен ли атрибут:
de.Properties["mobile"][0] = "+43 (664) 3434343434";
He забудьте вызвать метод DirectoryEntry.CommitChanges() после создания или обновления новых объектов каталога. Иначе обновляется только кэш, а изменения не посылаются службе каталога.
Создание новых объектов
Новые объекты активного каталога, такие как пользователи, компьютеры, принтеры, контакты и другие программным путем можно создать с помощью класса DirectoryEntries.
Чтобы добавить новые объекты в каталог, мы должны сначала соединиться с объектом-контейнером, подобным организационной единице, куда можно вставить новые объекты. Объекты, которые не могут содержать другие объекты, использовать нельзя. Здесь используется контейнерный объект с известным именем CN=Users, DC=eichkogelstrasse, DC=local:
DirectoryEntry de = new DirectoryEntry();
de.Path = "LDAP://celticrain/CN=Users, " +
"DC=eichkogelstrasse, DC=local";
Можно получить доступ к объекту DirectoryEntries с помощью свойства Children объекта DirectoryEntry:
DirectoryEntries users = de.Children;
Объект DirectoryEntries имеет методы для добавления, удаления, и поиска объектов в коллекции. Здесь создается новый объект пользователя. Для метода Add() нам нужно имя объекта и имя типа. Можно легко получить имена типов с помощью ADSI Edit.
DirectoryEntry user = users.Add("John Doe", "user");
Объект теперь имеет значения свойств по умолчанию. Чтобы присвоить специальные значения свойств, можно добавить свойства с помощью метода Add() свойства Properties. Конечно, все свойства должны существовать в схеме для объекта пользователя. Если определенное свойство не существует, то возникнет исключение COMException "The specified directory service attribute or value doesn't exist" ("Указанный атрибут или значение службы каталога не существует"). Если имена атрибутов правильны, но сервер отказывает во входе в связи с незаконным паролем или пропущенным свойством, исключение COMException будет содержать сообщение "The server is unwilling to process the request" ("Сервер не желает обрабатывать запрос").
user.Properties["company"].Add("Some Company");
user.Properties["department"].Add("Sales");
user.properties["employeeID"].Add("4711");
user.Properties["samAccountName"].Add("John Doe");
user.Properties["userPassword"].Add("someSecret");
В данный момент не все данные записаны в активный каталог. Необходимо очистить кэш:
user.CommitChanges();
Поиск в активном каталоге
Большую часть времени активный каталог будет использоваться для поиска некоторых значений. Это хранилище данных, которое оптимизировано в основном для чтения, поэтому данные будут в основном считываться.
Для поиска в активном каталоге платформа .NET имеет класс DirectorySearcher.
Мы можем использовать поиск только с провайдером LDAP. DirectorySearcher не работает с провайдерами NDS или IIS.
В конструкторе класса DirectorySearcher существуют четыре важные части поиска. Можно также использовать конструктор по умолчанию и определять параметры поиска с помощью свойств.
SearchRootКорень поиска (SearchRoot) определяет, где должен начаться поиск. По умолчанию SearchRoot является корнем домена, который используется в данный момент. SearchRoot определен с помощью свойства Path объекта DirectoryEntry.
FilterФильтр (Filter) определяет значения, которые мы хотим найти. Фильтр является строкой, которая должна быть заключена в круглые скобки.
Операторы отношений, такие как <=, =, >=, в выражениях допускаются. (objectClass = contact) будет искать все объекты типа contact; (lastName>=Nagel) ищет все объекты, где свойство lastName равно или больше Nagel, что означает, что оно следует за ним в алфавитном порядке.
Выражения могут комбинироваться с префиксными операциями & и |. (&(objectClass=user)(description=Auth*)) ищет все объекты типа user, где свойство description начинается со строки Auth. Так как операторы & и | находятся в начале выражения, то с помощью одного префиксного оператора можно комбинировать более двух выражений.
По умолчанию используется фильтр (objectClass=*), поэтому все объекты допустимы. Синтаксис фильтра определен в RFC 2254, "Строковое представление фильтров поиска LDAP". Этот RFC можно найти по адресу www.ietf.org/rfc/rfc2254.txt.
PropertiesToLoadС помощью PropertiesToLoad мы определяем коллекцию StringCollection всех интересующих нас свойств. Как вы уже видели, объекты могут иметь множество свойств. Большинство из них будут не важны для нашего запроса поиска. Мы определяем свойства, загружаемые в кэш. Свойствами по умолчанию, которые мы получаем, если ничего не определено, являются Path и Name для объекта.
SearchScopeSearchScope является перечислением, которое определяет, как глубоко должен распространяться поиск:
□ SearchScope.Base ищет атрибуты только в том объекте, где начинается поиск, поэтому мы получаем максимум один объект.
□ Для SearchScope.OneLevel поиск продолжается в коллекции-потомке базового объекта. Сам базовый объект для поиска не используется.
□ SearchScope.Subtree определяет, что поиск должен спускаться вниз по всему дереву.
По умолчанию для SearchScope используется Subtree.
Пределы поиска
Такой поиск может охватывать несколько доменов. Чтобы ограничить поиск некоторым числом объектов или требуемым временем, необходимо определить несколько дополнительных свойств.
Свойства DirectorySearcher Описание ClientTimeout Максимальное время, в течение которого клиент ожидает, что сервер вернет результат. Если сервер не отвечает, то никаких записей не возвращается. PageSize При постраничном поиске сервер возвращает число объектов, определенных с помощью PageSize, а не весь результат. Это сокращает и время клиента для получения первого ответа, и необходимую память. Сервер посылает клиенту cookie, которое отправляется назад на сервер с запросом следующего поиска, чтобы поиск можно было продолжить в точке, где он закончился. ServerPageTimeLimit Это значение определяет время для постраничного поиска, чтобы вернуть число объектов, которое определено значением PageSize. Если время истекает до достижения значения PageSize, найденные до этого момента объекты возвращаются клиенту. Значение по умолчанию равно -1, что означает бесконечность. ServerTimeLimit Определяет максимальное время, в течение которого сервер будет искать объекты. Когда это время истекает, все найденные до этого момента объекты возвращаются клиенту. По умолчанию используется 120 секунд, и нельзя задать время поиска больше этого значения. ReferalChasing Поиск может распространяться на несколько доменов. Если корень, который определен в SearchRoot, является родительским доменом или корень не был определен, поиск может распространиться на домены-потомки. С помощью этого свойства можно определить, что поиск должен продолжаться на других серверах. ReferalChasingOption.None означает, что поиск не продолжается на другие серверы. С помощью значения ReferalChasingOption.Subordinate можно определить, что поиск должен переходить на домены-потомки. Когда поиск начинается в DC=Wrox, DC=COM, сервер возвращает множество результатов и ссылку на DC=France, DC=Wrox, DC=COM. Клиент может продолжить поиск в поддомене. ReferalChasingOption.External означает, что сервер может направить клиента на независимый сервер, которого нет в поддомене. Это вариант поведения по умолчанию. Для ReferalChasingOption.All возвращаются ссылки на внешние домены и подчиненные домены.В рассматриваемом примере поиска мы хотим найти все объекты пользователей в организационной единице Wrox Press, где свойство description содержит значение Author.