C# System.DirectoryServices.AccountManagement并返回Active Directory用户的IEnumerable

C# System.DirectoryServices.AccountManagement并返回Active Directory用户的IEnumerable,c#,asp.net-mvc,active-directory,C#,Asp.net Mvc,Active Directory,我试图查询Active Directory并返回一个IEnumerable,但其属性(如Title和EmailAddress)未显示在PrincipalSearcher.Findll()中。如果我使用PrincipalSearcher.FindOne(),它有更多的属性(但仍然不是Title),因此我试图找出我的不同之处,或者如何获得我需要的信息。我已经厌倦了Google试图查找更多信息,它看起来像是UserPrincipal.GetUnderlyingObject()可能是一张罚单,但我不知道

我试图查询Active Directory并返回一个IEnumerable,但其属性(如Title和EmailAddress)未显示在PrincipalSearcher.Findll()中。如果我使用PrincipalSearcher.FindOne(),它有更多的属性(但仍然不是Title),因此我试图找出我的不同之处,或者如何获得我需要的信息。我已经厌倦了Google试图查找更多信息,它看起来像是UserPrincipal.GetUnderlyingObject()可能是一张罚单,但我不知道如何将其包含到foreach循环中,以便将其填充到列表中

    public class ADUser
    {
        public string SamAccountName { get; set; }
        public string DisplayName { get; set; }
        public string Title { get; set; }

        public IEnumerable<ADUser> Get(string username)
        {
            var users = new List<ADUser>();

            var principalContext = new PrincipalContext(ContextType.Domain, "domain.com");
            var userPrincipal = new UserPrincipal(principalContext)
            {
                SamAccountName = username
            };

            var principalSearcher = new PrincipalSearcher(userPrincipal);

            foreach (var user in principalSearcher.FindAll())
            {
                users.Add(new ADUser
                {
                    SamAccountName = user.SamAccountName,
                    DisplayName = user.DisplayName,
                    //Title = user.Title //Won't work, no Title property
                });
            }

            return users;
        }
    }
公共类ADUser
{
公共字符串SamAccountName{get;set;}
公共字符串DisplayName{get;set;}
公共字符串标题{get;set;}
公共IEnumerable Get(字符串用户名)
{
var users=新列表();
var principalContext=新的principalContext(ContextType.Domain,“Domain.com”);
var userPrincipal=新用户主体(principalContext)
{
SamAccountName=用户名
};
var principalSearcher=新principalSearcher(用户主体);
foreach(principalSearcher.FindAll()中的var user)
{
用户。添加(新用户)
{
SamAccountName=user.SamAccountName,
DisplayName=user.DisplayName,
//Title=user.Title//不起作用,没有Title属性
});
}
返回用户;
}
}

这可以工作,但只返回一小部分属性。FindOne()可以,但如果我使用FindOne(),我将无法搜索部分用户名,例如返回“John Smith”和“James Smoth”的“jsm”。

简单的回答是,您可以这样做:

        foreach (var user in principalSearcher.FindAll())
        {
            var userDe = (DirectoryEntry) user.GetUnderlyingObject();
            users.Add(new ADUser
            {
                SamAccountName = user.SamAccountName,
                DisplayName = user.DisplayName,
                Title = userDe.Properties["title"]?.Value.ToString()
            });
        }
这就是我不再使用
AccountManagement
名称空间的部分原因。它在后台使用
DirectoryEntry
,并隐藏其复杂性以使基本操作更简单,但对于某些操作,您仍然必须恢复直接使用
DirectoryEntry
。实际上,它的执行速度比直接使用
DirectoryEntry
/
DirectorySearcher
要慢

下面是一个示例,说明如何通过
DirectorySearcher
执行相同的操作。它有点复杂,但我打赌你会发现它执行得更快:

public class ADUser
{
    public string SamAccountName { get; set; }
    public string DisplayName { get; set; }
    public string Title { get; set; }

    public IEnumerable<ADUser> Get(string username)
    {
        var users = new List<ADUser>();

        var search = new DirectorySearcher(
            new DirectoryEntry("LDAP://domain.com"),
            $"(&(objectClass=user)(objectCategory=person)(sAMAccountName={username}))",
            new [] { "sAMAccountName", "displayName", "title" } //The attributes you want to see
        ) {
            PageSize = 1000 //If you're expecting more than 1000 results, you need this otherwise you'll only get the first 1000 and it'll stop
        };

        using (var results = search.FindAll()) {
            foreach (SearchResult result in results)
            {
                users.Add(new ADUser
                {
                    SamAccountName = result.Properties.Contains("sAMAccountName") ? result.Properties["sAMAccountName"][0].ToString() : null,
                    DisplayName = result.Properties.Contains("displayName") ? result.Properties["displayName"][0].ToString() : null,
                    Title = result.Properties.Contains("title") ? result.Properties["title"][0].ToString() : null
                });
            }
        }
        return users;
    }
}
公共类ADUser
{
公共字符串SamAccountName{get;set;}
公共字符串DisplayName{get;set;}
公共字符串标题{get;set;}
公共IEnumerable Get(字符串用户名)
{
var users=新列表();
var search=newdirectorysearcher(
新目录条目(“LDAP://domain.com”),
$“(&(objectClass=user)(objectCategory=person)(sAMAccountName={username}))”,
新建[]{“sAMAccountName”、“displayName”、“title”}//要查看的属性
) {
PageSize=1000//如果您期望的结果超过1000个,那么您需要这个结果,否则您只会得到前1000个结果,结果就会停止
};
使用(var results=search.FindAll()){
foreach(搜索结果中的搜索结果)
{
用户。添加(新用户)
{
SamAccountName=result.Properties.Contains(“SamAccountName”)?result.Properties[“SamAccountName”][0]。ToString():null,
DisplayName=result.Properties.Contains(“DisplayName”)?result.Properties[“DisplayName”][0]。ToString():null,
Title=result.Properties.Contains(“Title”)?result.Properties[“Title”][0]。ToString():null
});
}
}
返回用户;
}
}

也可能重复:这些将我引向正确的方向,但我仍然无法在foreach循环中获取Title属性。我可以通过“userPrincipal.Title”在外部看到它,但如果我不能将它放入列表中供以后迭代使用,这对我没有帮助。我猜当您使用用户名进行搜索时,您永远不会期望超过1000个结果:)因为您返回了一个列表,所以它将我甩了。无论如何,知道这一点是件好事。我得到一个错误:“ArgumentOutOfRangeException:索引超出范围。必须为非负且小于集合的大小。参数名:Index”当我尝试在完全用户名和部分用户名上运行该代码时。。我想这可能与“新[]”有关?我没有任何例外。哪一行为您抛出异常?如果您希望它也适用于部分用户名,您必须向筛选器添加一个
*
$”(&(objectClass=user)(objectCategory=person)(sAMAccountName={username}*)”
ADUser.cs的第35行,“users.add(new ADUser)”行。