问题:如何以一致的方式检索所有组成员?
上下文:我正在检索所有对象,组,联系人或计算机的对象:
Group Name Retrieval Method Recursive Search Count Members Comment
Group1 AccountManagement TRUE 505
Group1 AccountManagement FALSE 505
Group1 DirectoryServices N/A 101
Group2 AccountManagement TRUE 440 Contains group name 'Group3'
Group2 AccountManagement FALSE 440 Contains group name 'Group3'
Group2 DirectoryServices N/A 100 Contains group name 'Group3'
Group3 AccountManagement TRUE 101 All Group3
Group3 AccountManagement FALSE 2 All Group3
Group3 DirectoryServices N/A 2 1 user 1 group (Group2)
我现在需要检索所有组成员。我已经开发了三种方法来做到这一点;然而,他们正在为同一组返回不同的结果,我不知道为什么。我怀疑它可能是由嵌套组(即组内的组)引起的。调试它是一个挑战,因为有些组包含这么多成员,调试器超时并且没有显示结果。
方法1和2很慢。方法3很快。所以,我更喜欢使用方法3.
S.DS.AM
方法1和2 :使用private static List<Guid> GetGroupMemberList(string strPropertyValue, string strDomainController, bool bolRecursive)
{
List<Guid> listGroupMemberGuid = null;
GroupPrincipal groupPrincipal = null;
PrincipalSearchResult<Principal> listPrincipalSearchResult = null;
List<Principal> listPrincipalNoNull = null;
PrincipalContext principalContext = null;
ContextType contextType;
IdentityType identityType;
try
{
listGroupMemberGuid = new List<Guid>();
contextType = ContextType.Domain;
principalContext = new PrincipalContext(contextType, strDomainController);
identityType = IdentityType.Guid;
groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, identityType, strPropertyValue);
if (groupPrincipal != null)
{
listPrincipalSearchResult = groupPrincipal.GetMembers(bolRecursive);
listPrincipalNoNull = listPrincipalSearchResult.Where(item => item.Name != null).ToList();
foreach (Principal principal in listPrincipalNoNull)
{
listGroupMemberGuid.Add((Guid)principal.Guid);
}
}
return listGroupMemberGuid;
}
catch (MultipleMatchesException)
{
throw new MultipleMatchesException(strPropertyValue);
}
catch (Exception ex)
{
throw ex;
}
finally
{
listGroupMemberGuid = null;
listPrincipalSearchResult.Dispose();
principalContext.Dispose();
groupPrincipal.Dispose();
}
}
获取小组成员,其中GetMembers()分别设置为true或false:https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement(v=vs.110).aspx
S.DS.AD
方法3 :使用private static List<string> GetGroupMemberList(string strPropertyValue, string strActiveDirectoryHost, int intActiveDirectoryPageSize)
{
List<string> listGroupMemberDn = new List<string>();
string strPath = strActiveDirectoryHost + "/<GUID=" + strPropertyValue + ">";
DirectoryEntry directoryEntryGroup;
DirectoryEntry directoryEntryGroupMembers;
DirectorySearcher directorySearcher;
SearchResultCollection searchResultCollection;
DataTypeConverter objConverter = null;
objConverter = new DataTypeConverter();
try
{
directoryEntryGroup = new DirectoryEntry(strPath, null, null, AuthenticationTypes.Secure);
directoryEntryGroup.RefreshCache();
}
catch (Exception ex)
{
throw ex;
}
try
{
directorySearcher = new DirectorySearcher(directoryEntryGroup)
{
//Filter = "(objectCategory=group)", // Group
SearchScope = SearchScope.Subtree,
PageSize = intActiveDirectoryPageSize,
};
directorySearcher.PropertiesToLoad.Add("objectGUID");
searchResultCollection = directorySearcher.FindAll();
}
catch (Exception ex)
{
throw ex;
}
try
{
foreach (SearchResult searchResult in searchResultCollection)
{
directoryEntryGroupMembers = searchResult.GetDirectoryEntry();
foreach (object objGroupMember in directoryEntryGroupMembers.Properties["member"])
{
listGroupMemberDn.Add((string)objGroupMember);
}
}
return listGroupMemberDn;
}
catch (Exception ex)
{
throw ex;
}
finally
{
listGroupMemberDn = null;
strPath = null;
directoryEntryGroup.Dispose();
directoryEntryGroupMembers = null;
directorySearcher.Dispose();
searchResultCollection.Dispose();
objConverter = null;
}
}
获取小组成员:https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory(v=vs.110).aspx
private static List<string> GetGroupMemberList(string strPropertyValue, string strActiveDirectoryHost, int intActiveDirectoryPageSize)
{
// Variable declaration(s).
List<string> listGroupMemberDn = new List<string>();
string strPath = strActiveDirectoryHost + "/<GUID=" + strPropertyValue + ">";
string strMemberPropertyRange = null;
DirectoryEntry directoryEntryGroup = null;
DirectorySearcher directorySearcher = null;
SearchResultCollection searchResultCollection = null;
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms676302(v=vs.85).aspx
const int intIncrement = 1500;
// Load the DirectoryEntry.
try
{
// Setup a secure connection with Active Directory (AD) using Kerberos by setting the directoryEntry with AuthenticationTypes.Secure.
directoryEntryGroup = new DirectoryEntry(strPath, null, null, AuthenticationTypes.Secure);
// Load the property values for this DirectoryEntry object into the property cache.
directoryEntryGroup.RefreshCache();
}
catch (Exception)
{ }
#region Method1
// Enumerate group members.
try
{
// Check to see if the group has any members.
if (directoryEntryGroup.Properties["member"].Count > 0)
{
int intStart = 0;
while (true)
{
// End of the range.
int intEnd = intStart + intIncrement - 1;
strMemberPropertyRange = string.Format("member;range={0}-{1}", intStart, intEnd);
directorySearcher = new DirectorySearcher(directoryEntryGroup)
{
// Set the Filter criteria that is used to constrain the search within AD.
Filter = "(|(objectCategory=person)(objectCategory=computer)(objectCategory=group))", // User, Contact, Group, Computer objects
// Set the SearchScope for how the AD tree is searched (Default = Subtree).
SearchScope = SearchScope.Base,
// The PageSize value should be set equal to the PageSize that is set by the AD administrator (Default = 0).
PageSize = intActiveDirectoryPageSize,
PropertiesToLoad = { strMemberPropertyRange }
};
try
{
// Populate the searchResultCollection with all records within AD that match the Filter criteria.
searchResultCollection = directorySearcher.FindAll();
foreach (SearchResult searchResult in searchResultCollection)
{
var membersProperties = searchResult.Properties;
var membersPropertyNames = membersProperties.PropertyNames.OfType<string>().Where(n => n.StartsWith("member;"));
foreach (var propertyName in membersPropertyNames)
{
// Get all members from the ranged result.
var members = membersProperties[propertyName];
foreach (string memberDn in members)
{
// Add the member's "distinguishedName" attribute value to the list.
listGroupMemberDn.Add(memberDn);
}
}
}
}
catch (DirectoryServicesCOMException)
{
// When the start of the range exceeds the number of available results, an exception is thrown and we exit the loop.
break;
}
// Increment for the next range.
intStart += intIncrement;
}
}
// return the listGroupMemberDn;
return listGroupMemberDn;
}
#endregion
finally
{
// Cleanup objects.
listGroupMemberDn = null;
strPath = null;
strMemberPropertyRange = null;
directoryEntryGroup.Dispose();
directorySearcher.Dispose();
searchResultCollection.Dispose();
}
}
方法4 :(使用GetNextChunk()方法实现循环)
LoadControlsCallback
答案 0 :(得分:1)
System.DirectoryServices.AccountManagement
可以更方便,因为它隐藏了AD的大部分复杂性,但这也是它变慢的原因。你无法控制正在发生的事情。
DirectoryEntry
为您提供更多控制权,但您必须处理一些复杂性。
这样可以解释时差。
但是使用DirectoryEntry
的方法看起来仍然过于复杂。为什么要使用DirectorySearcher
?它似乎没有添加任何东西。设置directoryEntryGroup
时,您已拥有该组。之后,您可以访问成员:
foreach (var member in directoryEntryGroup.Properties["member"]) {
//member is a string of the distinguishedName
}
对于非常大的群体,请注意默认情况下,AD将其返回的记录限制为1500.因此,一旦您点击该数字,您将不得不要求更多。你这样做:
directoryEntryGroup.RefreshCache("member;range=1500-*")
然后以同样的方式再次循环它们。如果你得到另外1500,然后要求更多(用3000替换1500)等等,直到你拥有它们。
这正是System.DirectoryServices.AccountManagement
的.NET核心实现所做的(我认为.NET 4.x也是如此 - 我只是看不到代码)。您可以在此处看到.NET Core代码用于执行此操作的特殊类(请参阅GetNextChunk
方法):https://github.com/dotnet/corefx/blob/0eb5e7451028e9374b8bb03972aa945c128193e1/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/RangeRetriever.cs
作为旁注:
catch (Exception ex)
{
// Something went wrong. Throw an error.
throw ex;
}
如果您要在不做任何其他事情的情况下重新抛出异常,请不要抓住它。重新抛出具有隐藏异常实际发生位置的效果,因为您的堆栈跟踪现在会说明异常发生在throw ex;
,而不是告诉您发生异常的实际行。
即使是最后一个阻止版,也可以使用try
/ finally
而不使用catch
。