【问题标题】:Using PrincipalSearcher to find users with "or" parameters使用 PrincipalSearcher 查找带有“或”参数的用户
【发布时间】:2012-05-15 18:02:38
【问题描述】:

是否可以使用System.DirectoryServices.AccountManagement.PrincipalSearcher 使用“或”(不是“与”)基于多个参数进行搜索。

// This uses an and
//(&(objectCategory=person)(!UserAccountControl:1.2.840.113556.1.4.803:=2)(&(SAMAccountName=tom*)(DisplayName=tom*)))
var searchPrinciple = new UserPrincipal(context);
searchPrinciple.DisplayName =  "tom*";
searchPrinciple.SamAccountName = "tom*";

var searcher = new PrincipalSearcher();
searcher.QueryFilter = searchPrinciple;

var results = searcher.FindAll();

我想使用PrincipalSearcher(不是DirectorySearcher)进行类似的搜索(在LDAP中)

// (&(objectCategory=person)(!UserAccountControl:1.2.840.113556.1.4.803:=2)(|(SAMAccountName=tom*)(DisplayName=tom*)))

【问题讨论】:

    标签: c# .net active-directory


    【解决方案1】:

    这显然是不可能的,这里有一个解决方法:

    List<UserPrincipal> searchPrinciples = new List<UserPrincipal>();
    searchPrinciples.Add(new UserPrincipal(context) { DisplayName="tom*"});
    searchPrinciples.Add(new UserPrincipal(context) { SamAccountName = "tom*" });
    searchPrinciples.Add(new UserPrincipal(context) { MiddleName = "tom*" });
    searchPrinciples.Add(new UserPrincipal(context) { GivenName = "tom*" });
    
    List<Principal> results = new List<Principal>();
    var searcher = new PrincipalSearcher();
    foreach (var item in searchPrinciples)
    {
        searcher = new PrincipalSearcher(item);
        results.AddRange(searcher.FindAll());
    }
    

    【讨论】:

    • 您必须使用它来处理重复项。如果显示名称、名字和帐户名称都包含名称“tom”,则您将有重复。
    • 我也想要这个,尽管在多个查询点它的性能明显降低了吗?我想知道如果 PrincipalSearcher 无法完成,为什么我不应该退回到 DirectorySearcher
    【解决方案2】:

    不一定像其他一些答案那样干净,但这是我在我正在从事的项目中实现这一点的方式。我希望两个搜索都异步运行,以尝试减少由于运行两个 AD 查询而导致的任何减速。

    public async static Task<List<ADUserEntity>> FindUsers(String searchString)
    {
        searchString = String.Format("*{0}*", searchString);
        List<ADUserEntity> users = new List<ADUserEntity>();
    
        using (UserPrincipal searchMaskDisplayname = new UserPrincipal(domainContext) { DisplayName = searchString })
        using (UserPrincipal searchMaskUsername = new UserPrincipal(domainContext) { SamAccountName = searchString })
        using (PrincipalSearcher searcherDisplayname = new PrincipalSearcher(searchMaskDisplayname))
        using (PrincipalSearcher searcherUsername = new PrincipalSearcher(searchMaskUsername))
        using (Task<PrincipalSearchResult<Principal>> taskDisplayname = Task.Run<PrincipalSearchResult<Principal>>(() => searcherDisplayname.FindAll()))
        using (Task<PrincipalSearchResult<Principal>> taskUsername = Task.Run<PrincipalSearchResult<Principal>>(() => searcherUsername.FindAll()))
        {
            foreach (UserPrincipal userPrincipal in (await taskDisplayname).Union(await taskUsername))
                using (userPrincipal)
                {
                    users.Add(new ADUserEntity(userPrincipal));
                }
        }
    
        return users.Distinct().ToList();
    }
    

    我的 ADUserEntity 类具有基于 SID 的相等性检查。我尝试将Distinct() 添加到两个搜索结果的Union() 上,但没有成功。

    我欢迎对我的回答提出任何建设性的批评,因为我想知道是否有任何方法可以改进它。

    【讨论】:

    • 要仅返回不同的用户,您可以使用 LINQ 按标识符(例如 SID)进行分组:将 return users.Distinct().ToList(); 替换为 return users.GroupBy(user =&gt; user.Sid).Select(group =&gt; group.First()).ToList();
    • 在将 ADUserEntity 对象添加到集合之前使用简单的 if 语句怎么样? if (!users.Contains(userPrincipal) users.Add(new ADUserEntity(userPrincipal));
    【解决方案3】:

    我知道这有点晚了,但这是我在搜索 AD 时使用的结构:

    public static Task<IEnumerable<SomeUserModelClass>> GetUsers(//Whatever filters you want)
    {
        return Task.Run(() =>
        {
            PrincipalContext context = new PrincipalContext(ContextType.Domain);
            UserPrincipal principal = new UserPrincipal(context);
            principal.Enabled = true;
            PrincipalSearcher searcher = new PrincipalSearcher(principal);
    
            var users = searcher.FindAll().Cast<UserPrincipal>()
                .Where(x => x.SomeProperty... // Perform queries)
                .Select(x => new SomeUserModelClass
                {
                    userName = x.SamAccountName,
                    email = x.UserPrincipalName,
                    guid = x.Guid.Value
                }).OrderBy(x => x.userName).AsEnumerable();
    
            return users;
        });
    }
    

    【讨论】:

    • 您基本上是在读取整个目录,并在客户端执行过滤。这不适用于任何中型和更大的目录。
    • 我已经添加,在执行搜索之前,过滤到 UserPrincipal 对象并将搜索范围缩小到特定的起始 OU。
    【解决方案4】:

    FindAll 方法搜索主体中指定的域 上下文对于那些具有相同属性的对象 查询过滤器。 FindAll 方法返回所有匹配的对象 提供的对象,而 FindOne 方法只返回一个 匹配主体对象。 http://msdn.microsoft.com/en-us/library/bb384378(v=vs.90).aspx

    我不知道您需要什么,但您可以按 1 个属性和 1 个其他属性进行搜索,然后在列表中使用 LINQ 进行合并、过滤等...

    【讨论】:

    • 是的,这是我目前正在使用的解决方法,我希望有一种方法可以在一次搜索中更干净地做到这一点。不过还是谢谢。
    【解决方案5】:
    PrincipalContext pContext = new PrincipalContext(ContextType.Machine, Environment.MachineName);
    GroupPrincipal gp = GroupPrincipal.FindByIdentity(pContext, "Administrators");
    bool isMember = UserPrincipal.Current.IsMemberOf(gp);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-25
      • 1970-01-01
      • 2015-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多