Difference between PrincipalSearcher and DirectorySearcher

后端 未结 3 1815
耶瑟儿~
耶瑟儿~ 2020-12-22 19:02

I see Active Directory examples that use PrincipalSearcher and other examples that do the same thing but use DirectorySearcher. What is the differe

相关标签:
3条回答
  • 2020-12-22 19:19

    DirectorySearcher is by far faster. The example from @DrewChapin can be taken even further. By my tests, about 10 times further/faster. I was able to pull 721 computer cn's in 3.8 seconds with his code. Pretty fast. With my changes I did it in 0.38 seconds. Depending on what you're doing, this could be huge. I used this in a predictive account search (Start typing the name and a combobox populates. A very short System.Timer starts after each keypress and is cancelled by a keypress. If the timer elapses, the list updates. Efficiency is huge for this.)

    DirectoryEntry entry = new DirectoryEntry();
    DirectorySearcher search = new DirectorySearcher(entry,queryString);
    search.PageSize = 1000;
    // *** Added following line
    search.PropertiesToLoad.AddRange(new string[] { "canonicalName" });
    foreach( SearchResult result in search.FindAll() )
    {
        //DirectoryEntry user = result.GetDirectoryEntry();
        // *** Work directly with result instead of user
        if( result != null )
        {
            //user.RefreshCache(new string[]{"canonicalName"});
            // *** Following line modified
            string canonicalName = result.Properties["canonicalName"][0].ToString();
            count++;
        }
    }
    
    0 讨论(0)
  • 2020-12-22 19:20

    I've spent a lot of time analyzing the differences between these two. Here's what I've learned.

    • DirectorySearcher comes from the System.DirectoryServices namespace.

    • PrincipalSearcher comes from the System.DirectoryServices.AccountManagement namespace, which is built on top of System.DirectoryServices. PrincipalSearcher internally uses DirectorySearcher.

    • The AccountManagement namespace (i.e. PrincipalSearcher) was designed to simplify management of User, Group, and Computer objects (i.e. Principals). In theory, it's usage should be easier to understand, and produce fewer lines of code. Though in my practice so far, it seems to heavily depend on what you're doing.

    • DirectorySearcher is more low-level and can deal with more than just User, Group and Computer objects.

    • For general usage, when you're working with basic attributes and only a few objects, PrincipalSearcher will result in fewer lines of code and faster run time.

    • The advantage seems to disappear the more advanced the tasks you're doing become. For instance if you're expecting more than few hundred results, you'll have to get the underlying DirectorySearcher and set the PageSize

      DirectorySearcher ds = search.GetUnderlyingSearcher() as DirectorySearcher;
      if( ds != null )
          ds.PageSize = 1000;
      
    • DirectorySearcher can be significantly faster than PrincipalSearcher if you make use of PropertiesToLoad.

    • DirectorySearcher and like classes can work with all objects in AD, whereas PrincipalSearcher is much more limited. For example, you can not modify an Organizational Unit using PrincipalSearcher and like classes.

    Here is a chart I made to analyze using PrincipalSearcher, DirectorySearcher without using PropertiesToLoad, and DirectorySearcher with using PropertiesToLoad. All tests...

    • Use a PageSize of 1000
    • Query a total of 4,278 user objects
    • Specify the following criteria
      • objectClass=user
      • objectCategory=person
      • Not a scheduling resource (i.e. !msExchResourceMetaData=ResourceType:Room)
      • Enabled (i.e. !userAccountControl:1.2.840.113556.1.4.803:=2)

    DirectorySearcher vs. PrincipalSearcher Performance Chart


    Code For Each Test


    Using PrincipalSearcher

    [DirectoryRdnPrefix("CN")]
    [DirectoryObjectClass("Person")]
    public class UserPrincipalEx: UserPrincipal
    {
    
        private AdvancedFiltersEx _advancedFilters;
    
        public UserPrincipalEx( PrincipalContext context ): base(context)
        {
            this.ExtensionSet("objectCategory","User");
        }
    
        public new AdvancedFiltersEx AdvancedSearchFilter
        {
            get {
                if( null == _advancedFilters )
                    _advancedFilters = new AdvancedFiltersEx(this);
                    return _advancedFilters;
            }
        }
    
    }
    
    public class AdvancedFiltersEx: AdvancedFilters 
    {
    
        public AdvancedFiltersEx( Principal principal ): 
            base(principal) { }
    
        public void Person()
        {
            this.AdvancedFilterSet("objectCategory", "person", typeof(string), MatchType.Equals);
            this.AdvancedFilterSet("msExchResourceMetaData", "ResourceType:Room", typeof(string), MatchType.NotEquals);
        }
    }
    
    //...
    
    for( int i = 0; i < 10; i++ )
    {
        uint count = 0;
        Stopwatch timer = Stopwatch.StartNew();
        PrincipalContext context = new PrincipalContext(ContextType.Domain);
        UserPrincipalEx filter = new UserPrincipalEx(context);
        filter.Enabled = true;
        filter.AdvancedSearchFilter.Person();
        PrincipalSearcher search = new PrincipalSearcher(filter);
        DirectorySearcher ds = search.GetUnderlyingSearcher() as DirectorySearcher;
        if( ds != null )
            ds.PageSize = 1000;
        foreach( UserPrincipalEx result in search.FindAll() )
        {
            string canonicalName = result.CanonicalName;
            count++;
        }
    
        timer.Stop();
        Console.WriteLine("{0}, {1} ms", count, timer.ElapsedMilliseconds);
    }
    


    Using DirectorySearcher

    for( int i = 0; i < 10; i++ )
    {
        uint count = 0;
        string queryString = "(&(objectClass=user)(objectCategory=person)(!msExchResourceMetaData=ResourceType:Room)(!userAccountControl:1.2.840.113556.1.4.803:=2))";
    
        Stopwatch timer = Stopwatch.StartNew();
    
        DirectoryEntry entry = new DirectoryEntry();
        DirectorySearcher search = new DirectorySearcher(entry,queryString);
        search.PageSize = 1000;
        foreach( SearchResult result in search.FindAll() )
        {
            DirectoryEntry user = result.GetDirectoryEntry();
            if( user != null )
            {
                user.RefreshCache(new string[]{"canonicalName"});
                string canonicalName = user.Properties["canonicalName"].Value.ToString();
                count++;
            }
        }
        timer.Stop();
        Console.WriteLine("{0}, {1} ms", count, timer.ElapsedMilliseconds);
    }
    


    Using DirectorySearcher with PropertiesToLoad

    Same as "Using DirectorySearcher but add this line

    search.PropertiesToLoad.AddRange(new string[] { "canonicalName" });
    

    After

    search.PageSize = 1000;
    
    0 讨论(0)
  • 2020-12-22 19:45

    PrincipalSearcher is used to query the Directory for Groups or Users. DirectorySearcher is used to query all kinds of objects.

    I used DirectorySearcher to get groups before then I discovered PrincipalSearcher so when I replaced the former with the latter, the speed of my program improved (maybe it was just PrincipalSearcher that created a better query for me. For what I care, PrincipalSearcher was just easier to use and more suitable for the task of getting pricipals.

    DirectorySearcher on the other hand is more general as it can get other kinds of objects. This is why it can't be strongly typed as mentioned in the comments. PrincipalSearcher is all about principals so it will have strongly typed objects that pertain to principals, and this is why also you don't need to tell it to get you an object of kind user or group, it will be implied by the Principal classes you use.

    0 讨论(0)
提交回复
热议问题