# 五、`System.DirectoryServices`

## 主要类别和基本概念

**`System.DirectoryServices`**是一个命名空间，包含允许我们从托管代码访问活动目录的类和方法。这个名称空间的核心组件是**目录尝试**和**目录射手**类；接下来，我们将特别关注这些类中的每一个，提供更多关于它们的方法的信息，更重要的是，它们的核心功能。这两个类都使用 ADSI 技术，并且都可以与任何活动目录域服务提供商一起使用。目前的提供商有互联网信息服务(IIS)、轻量级目录访问协议(LDAP)、Novell NetWare 目录服务(NDS)和 WinNT。

## 导演射手类

您可以使用目录搜索对象来搜索和执行对任何符合 LDAP 的服务器的查询，特别是对活动目录域服务的查询。LDAP 是 ADSI 提供的唯一支持目录搜索的系统提供商。创建目录搜索程序时，指定要检索的根目录和可选的要检索的属性列表。该任务由 **SearchRoot** 属性执行。

SearchRoot 属性使您能够设置其他属性来执行以下任务:

*   在本地计算机上缓存搜索结果。将 **CacheResults** 属性设置为 true，以在本地计算机上存储目录信息。只有当**目录尝试时，才会对此本地缓存进行更新。调用 CommitChanges** 方法。
*   使用**服务器时间限制**属性指定搜索时间长度。
*   仅检索属性名。将**属性名称仅**属性设置为真，仅检索已赋值的属性名称。
*   执行分页搜索。设置**页面大小**属性，指定页面搜索中返回的最大对象数。如果不想执行分页搜索，请将 PageSize 属性设置为零(这是默认设置)。
*   使用**大小限制**属性指定要返回的最大条目数。如果将**大小限制**属性设置为默认值零，则服务器确定的默认值为 1000 个条目。

| ![](img/note.png) | 注意:如果返回条目的最大数量和时间限制超过了服务器上设置的限制，服务器设置将覆盖组件设置。 |

### find one 方法

FindOne 方法执行搜索，只返回找到的第一个条目。如果在搜索过程中找到多个条目，则只返回第一个条目。如果没有找到符合搜索条件的条目，将返回空引用。

### 查找所有方法

FindAll 方法执行搜索并返回找到的条目的集合。然而，由于我们可以处理的提供商的多样性，这一操作有可能无法成功完成；在这种情况下，将引发 NotSupportedException。另一个不成功的场景是当我们试图在不是容器的对象中执行搜索时；在这种情况下，FindAll 方法会引发 InvalidOperationException。

### 客户端超时属性

此属性表示客户端等待服务器返回结果的最长时间。如果服务器在此时间内没有响应，搜索将中止，并且不会返回任何结果。

### 服务器时间限制属性

此属性获取或设置一个值，该值指示服务器用于搜索的最长时间。如果达到时间限制，则只返回在该时间点之前找到的条目。

### 属性加载属性

此属性是字符串的集合(确切的类型名称是 StringCollection)，它获取一个值，该值指示搜索期间要检索的属性列表。

### 墓碑物业

此属性获取或设置一个值，该值指示搜索是否还应返回与搜索筛选器匹配的已删除对象。如果您不知道广告中的对象是否已被删除或仍然存在，此选项会非常有帮助。

## 导演尝试类

您可以使用目录尝试类绑定到活动目录对象，以利用读取和更新属性等功能。这个类以及一些帮助类为生命周期管理和导航方法提供了支持。

生命周期，我们指的是子节点的完整管理(创建、删除、重命名和移动)；您还能够枚举一个节点的所有子节点。当您考虑一组对象(如用户、计算机或打印机)时，这很有用。为了将更改传播到广告树，您必须提交您的更改。与我们的案例场景(阿尔法数据公司)一样，现实世界中的公司正在不断扩张，向他们的森林或域中添加新的员工或计算机；ADSI 技术提供了以编程方式将这些对象添加到目录树的方法。

### 创建子节点

要在层次结构中创建目录条目，请使用 Children 属性。Children 属性是一个集合，它提供了一个 Add 方法，通过该方法可以将节点添加到集合中当前绑定到的父节点的正下方。

向集合中添加节点时，必须指定新节点的名称以及要与该节点关联的架构模板的名称。例如，您可能希望使用名为“计算机”的模式将新计算机添加到层次结构中。此类还包含属性缓存，这对于优化网络流量非常有用。要使用属性缓存，请参见 **UsePropertyCache** 属性。

### 承诺变更法

此方法将对目录条目所做的更改保存到基础目录存储中。

| ![](img/note.png) | 注意:如果在调用 CommitChanges 之前调用 RefreshCache，对属性缓存的任何未提交的更改都将丢失。 |
| ![](img/tip.png) | 提示:使用目录服务会话类来读取、写入、删除、更改和添加到活动目录域服务层次结构。关联枚举:目录服务授权访问。 |

### 复制法

CopyTo 方法的第一个重载只接受另一个 DirectoryEntry 作为参数，它创建此条目的副本作为指定父条目的子条目。第二个重载将字符串作为参数，指定新复制的节点的名称。此方法返回复制的目录尝试的实例，并在要复制到的指定目录尝试不是容器的情况下引发无效操作异常。为了反映您对广告树的更改，您必须调用 CommitChanges 方法。

### 删除树方法

此方法从活动目录域服务层次结构中删除条目及其整个子树。在活动目录服务接口(ADSI)系统提供的提供程序中，目前只有轻型目录访问协议(LDAP)支持此操作。

### 调用、调用获取和调用集合方法

这些方法用于从托管代码中调用本机方法或 getter 和 setter 属性。

1.  **Invoke** 方法采用要调用的方法的名称和定义列表的对象数组作为其参数。
2.  **InvokeGet** 方法获取您想要获取其值的属性的名称(字符串形式)。
3.  **InvokeSet** 方法的不同之处仅在于方法坚定；它处理一个对象数组，这些对象用作您指定的属性的可能值。

如果失败，调用方法可能会抛出两种类型的异常。第一个是目录服务异常，当本机方法抛出 COMException 时。第二种是 TargetInvocationException，当本机方法引发 TargetInvocationException 异常时。InnerException 属性包含一个 COMException，它包含有关实际发生的错误的信息。

### 移动到方法

像**复制到**方法一样，**移动到**方法有两个重载。第一个只接受一个目录尝试，只需将调用目录尝试移动到作为参数传递的目录尝试中。第二个重载像在 CopyTo 的情况下一样，接受一个字符串参数，该参数指定要在新的 DirectoryEntry 父级中使用的名称。如果作为参数传递的 DirectoryEntry 不是容器，MoveTo 方法会抛出一个**invalid operationexception**。

### 重命名方法

重命名方法更改此目录对象的名称。

| ![](img/note.png) | 注意:这也将影响用于引用该条目的路径。 |

## 搜索结果类

**搜索结果**类封装了活动目录域服务层次结构中的一个节点，该节点在通过目录搜索程序进行搜索时返回。SearchResult 类的实例与 DirectoryEntry 类共享一种类似的方法来访问对象属性。最重要的区别是，每次访问新对象时，目录尝试类都会从活动目录域服务层次结构中检索其信息，而搜索结果的数据已经在**搜索结果集合**中可用，它是从使用目录搜索类执行的查询中返回的。只有那些通过目录服务器指定的属性。查询中的属性加载集合可从搜索结果中获得。

### 路径属性

获取此搜索结果的路径。路径属性在活动目录域服务层次结构中唯一标识此条目。始终可以使用此路径检索条目。

### 属性属性

这将获取此对象的属性的 ResultPropertyCollection 集合；当然，这个集合只包含通过 directory archer . properties to load 显式请求的属性。ResultPropertyCollection 继承自 DictionaryBase 类，因此可以在 Item 属性的帮助下使用<key value="">符号访问其成员。在 ResultPropertyCollection 中，我们还有一些字段可以用来获取字典中包含的项目数，所有的属性名(之前是通过 DirectorySearcher 指定的。属性加载)、键和值。</key>

### GetDirectoryEntry 方法

从活动目录域服务层次结构中检索与搜索结果对应的目录尝试。

| ![](img/note.png) | 注意:对通过目录服务返回的每个搜索结果调用 GetDirectoryEntry 可能会很慢。 |

## 搜索结果集合类

**搜索结果集合**类包含活动目录层次结构在目录搜索查询期间返回的**搜索结果**实例。因为它继承自集合，所以它具有 Count 和 Item 属性，这两个属性分别表示集合中包含的项数和集合中的项本身(给定有效的索引)。即使在 SearchResultCollection 内部，使用属性加载字段，我们也可以获得在 directory archer . properties to load 中显式指定的属性列表。

| ![](img/note.png) | 注意:由于实现限制，当垃圾收集时，SearchResultCollection 类无法释放其所有非托管资源。为了防止内存泄漏，当不再需要 SearchResultCollection 对象时，必须调用 Dispose 方法。 |

## 用户主体类

**UserPrincipal** 类封装了作为用户帐户的主体。我们可以将主体定义为 AD 对象。我们可以使用这个类来表示一个用户对象，从更通用的类目录开始。UserPrincipal 类有一个主构造函数，它将 PrincipalContext(参见下一章)的引用作为输入，在它的变体中有一个用户名、一个密码和一个布尔标志。

用户主体类包含在**系统中。目录服务.账户管理**命名空间。

用户主体类公开了一些非常有用的静态方法，允许我们基于特定的属性或条件执行搜索操作。所有这些静态方法都将 PrincipalContext 对象、定义搜索条件的 MatchType 标志以及因方法而异的第三个参数作为参数；其中一些解释如下。

### findbybbaddpasswortry 方法

此方法搜索在指定为参数的日期时间范围内有错误密码记录的用户；匹配的类型可以使用 MatchType 枚举来定义。如果找不到结果，此方法将返回一个空的 user principal resultcollection。

### FindByExpirationTime 方法

此方法在给定日期时间范围的情况下，返回**用户主体**的 **PrincipalSearchResult** 对象，该对象的帐户到期日期满足匹配类型枚举所施加的筛选。

### findbylockoutime 方法

对于帐户锁定时间在指定日期和时间范围内的用户，该方法返回**用户主体**的**主用户结果**对象。即使在这种情况下，**匹配类型**属性定义了搜索条件，如果结果为零，将返回一个空集合。

### findbylogentime 方法

此方法返回用户主体对象的 **PrincipalSearchResult** 集合，这些用户的帐户登录记录在指定的日期和时间范围内。

除了这些静态方法之外，UserPrincipal 类使我们更容易对用户对象执行操作，例如更改/终止他们的密码、检查组成员资格以及列出组成员资格。此外，UserPrincipal 类以更面向对象的方式封装了用户对象的属性 getter。

| ![](img/note.png) | 注:关于 UserPrincipal 类的更多信息可以在官方 MSDN 页面的[中找到。](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.userprincipal(v=vs.110).aspx) |

## principal context 类

此类封装了执行所有操作的服务器或域、用作这些操作的基础的容器以及用于执行这些操作的凭据。这个类本身不公开任何特定的方法或属性，但是它有一个有价值的方法，可以声明一组凭据(用户名和密码)是否有效。这种验证是通过执行对服务器的连接请求来执行的，该连接请求在方法的签名中被指定为参数。

```cs
          PrincipalContextprincipalContext = null;
          try
          {
              principalContext = new PrincipalContext(ContextType.Domain, IP_PORT, "CN =Users,DC=alphadata,DC=adds");
              bool isValid = principalContext.ValidateCredentials("user", "pwd");
          }
          catch (PrincipalException e)
          { Console.WriteLine("Failed to create PrincipalContext. Exception: " + e); return; }

```

**上下文类型**参数可以是机器、域或应用程序目录。请注意，由于可能会引发 PrincipalException(例如，当与服务器的连接不成功时)，所以一切都被包装在 try-catch 子句中。 **IP_PORT** 参数是一个常量，包含广告服务器的本地 IP 地址和 LDAP 端口(389)。

## 组主体和计算机主体类

由于 AD 允许我们管理组和计算机，系统。AccountManagement 命名空间提供了两个从名为 Principal 的抽象类继承的类来管理它们。这些类是 GroupPrincipal 和 ComputerPrincipal，正如我们在 UserPrincipal 类中看到的，它们的主要功能是以一种更简单、更面向对象的方式包装 AD/DirectoryEntry 对象。在这种情况下，它可以是一个组或一台计算机。根据类引用的对象类型，您可以公开不同的方法和属性。注意到 GroupPrincipal 和 ComputerPrincipal 都继承自与 UserPrincipal 相同的基类这一事实，我们将不继续描述它们的公共(和静态)方法，因为它们是相似的，并且在某些情况下与本章前面讨论的方法相同。

| ![](img/note.png) | 注意:如果您想详细了解 GroupPrincipal 和 ComputerPrincipal 类，请参考以下链接:group principal:[http://msdn . Microsoft . com/en-us/library/system . director yservices . account management . group principal(v = vs . 110)。aspx](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.groupprincipal(v=vs.110).aspx)computer principal:[http://msdn . Microsoft . com/en-us/library/system . director yservices . account management . computer principal(v = vs . 110)。aspx](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.computerprincipal(v=vs.110).aspx) |

## 匹配类型枚举

当使用静态方法在 **PrincipalContext** 中执行查询时，有必要指定一个特定的标志来定义我们要考虑的匹配类型。下表列出了**匹配类型**枚举的所有可能值。

| 成员名称 | 描述 |
| --- | --- |
| 等于 | 搜索结果包括与提供的值相等的值。如果提供的值指定了日期和时间，则返回的集合包括具有相同日期和时间的对象。 |
| 大于 | 搜索结果包含的值大于提供的值。如果提供的值指定了日期和时间，则返回的集合包括日期在指定日期和时间之后的对象。 |
| 更大的需求 | 搜索结果包括大于或等于提供值的值。如果提供的值指定了日期和时间，则返回的集合包括日期在指定日期和时间或之后的对象。 |
| 少于 | 搜索结果包括小于提供值的值。如果提供的值指定了日期和时间，则返回的集合包括日期早于指定日期和时间的对象。 |
| 至少同等 | 搜索结果包括小于或等于提供值的值。如果提供的值指定了日期和时间，则返回的集合包括日期早于或等于指定日期和时间的对象。 |
| 注意质量 | 搜索结果包含的值不等于提供的值。如果提供的值指定了日期和时间，则返回的集合包含不包含指定日期和时间的对象。 |

表 3:匹配类型枚举的成员列表