1

I want to get information from only 1 user out of 20,000 users. The response time of the method I used below is 40 seconds. What is the solution to this problem?

public AuthenticatedUserProperties Info(string Username)
        {
            try
            {
                var context = new PrincipalContext(ContextType.Domain, Settings.LDAPDomain, Settings.LDAPContainer, Settings.LDAPUsername, Settings.LDAPPassword);

                UserPrincipal user = new UserPrincipal(context);
                user.SamAccountName = Username;
                var searcher = new PrincipalSearcher(user);
                var searchResults = searcher.FindOne();
                DirectoryEntry de = searchResults.GetUnderlyingObject() as DirectoryEntry;
                ActiveDirectoryUserProperties prop = ConvertLdapUserPropertyToArray(de);

                return new AuthenticatedUserProperties
                {
                    Status = true,
                    Properties = prop
                };

            }
            catch (Exception e)
            {

                return new AuthenticatedUserProperties
                {
                    Status = false,
                    Properties = null
                };
            }
        }
Gökhan YILDIZ
  • 131
  • 3
  • 16
  • Probably directly accessing the underlying DirectoryEntry. What does `ConvertLdapUserPropertyToArray` do? Also you could use `UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, Username);` instead of a `PrincipalSearcher`. – The-First-Tiger Jul 24 '18 at 10:19
  • ConvertLdapUserPropertyToArray makes the relevant data meaningful and returns it as an array. But my problem is starting to requesting user properties. I think is trying to pull 20000 users first and after then finding requested user. This process responding 40-50 seconds after. – Gökhan YILDIZ Jul 24 '18 at 11:32
  • 2
    The code isnt processing 20000 users. It asks the server to find 1 user with specific searchcriteria. Identify if the majority of the 40s is consumed by searching or by `ConvertLdapUserPropertyToArray`. If u process a lot of props in `ConvertLdapUserPropertyToArray` using underlying DirectoryEntry this can be slow cause accessing most property values starts a new request to the ldap server. You can override UserPrincipal and add the properties you require or you could use 'DirectoryServices' instead of 'AccountingManagement' and use a `DirectorySearcher` with preconfigured properties to load. – The-First-Tiger Jul 24 '18 at 13:35
  • I can not get the debug result on the server healthy because I can not access it with my own computer. They do not allow this process for security reasons. I have no problem on the active directory installed on the virtual machine. But I am having problems with the production server. You can see the ConvertLdapUserPropertyToArray function from the URL. https://codeshare.io/21NDR0 Do you think this use will cause a slowdown? – Gökhan YILDIZ Jul 24 '18 at 14:43
  • Hard to tell just from looking at the code. Also it seems there is a database call also. Perf optimisation without measureing doesnt make lot of sense. You could give `DirectorySearcher` a try as it preloads the properties this could boost performance but I don't know if this is really the bottleneck but searching for the user shouldn't be. Here someone says "Querying some thousand users,[...](around 30 seconds for ~34k users)" https://stackoverflow.com/questions/45357892/ Sample implementation: https://codeshare.io/am3L0X Took 170 ms searching the user ~3k users 2.5 converting the props – The-First-Tiger Jul 24 '18 at 19:56
  • I changed the code like on the link. https://codeshare.io/am3L0X Code The virtual machine also works fine. I will take the test result on Monday. I hope that it will work more faster in this situation. I will comment again according to the test result. Thank you for your help and ideas. – Gökhan YILDIZ Jul 25 '18 at 06:04
  • https://codeshare.io/am3L0X not found? – Kiquenet Oct 26 '22 at 07:47

1 Answers1

2

I use the System.DirectoryServices.Protocols library instead. It is always blazing fast. I can never get System.DirectoryServices.AccountManagement to have reliable performance and it is often agonizingly slow (10+ seconds) to get just one user. TBH - I think our Network setup is likely to blame causing the bind to be dysfunctional - but the Protocols library yields good results without much effort regardless of our network dysfunction.

You have to do slightly more work - but nothing particularly difficult. I'm not an expert with this library - but this sample code works reliably for me.

using System.DirectoryServices.Protocols;
public class UserInfo
{
    public string SAMAccountName;
    public string DomainHostName;
    public string ADSDirectory;
    public Dictionary<string, string> UserAttributes;
    // Some attributes not really strings and require extra handling - but simplied for example
    // This is really just for illustrative purposes
    public UserInfo(string a_SAMAccountName, string a_DomainHostName = "ldap.mydomain:3268", string a_ADSDirectory = "ours.net")
    {
        UserAttributes = new Dictionary<string, string>();
        SAMAccountName = a_SAMAccountName;
        DomainHostName = a_DomainHostName;
        ADSDirectory = a_ADSDirectory;
    }
}
public static class GetUserAttributes
{
    public static List<string> WantedAttributes;

    static GetUserAttributes()
    {
        WantedAttributes = new List<string>();
        WantedAttributes.Add("mail");
        //... Add Properties Wanted
    }

    public static void GetUserAttributes(UserInfo a_user)
    {
        using (HostingEnvironment.Impersonate())
        {
            LdapDirectoryIdentifier z_entry = new LdapDirectoryIdentifier(a_user.DomainHostName, true, false);
            using (LdapConnection z_remote = new LdapConnection(z_entry))
            {
                z_remote.SessionOptions.VerifyServerCertificate = delegate (LdapConnection l, X509Certificate c) { return true; };
                z_remote.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
                z_remote.SessionOptions.ProtocolVersion = 3;
                z_remote.Bind();

                SearchRequest z_search = new SearchRequest();
                z_search.Scope = System.DirectoryServices.Protocols.SearchScope.Subtree;
                z_search.Filter = "(SAMAccountName=" + a_user.SAMAccountName + ")";
                z_search.DistinguishedName = a_user.ADSdirectory;

                foreach (List<string> z_item in WantedAttributes)
                {
                    z_search.Attributes.Add(z_item);
                }

                SearchResponse z_response = (SearchResponse)z_remote.SendRequest(z_search);

                if (z_response != null)
                {
                    foreach (SearchResultEntry z_result in z_response.Entries)
                    {
                        foreach (string z_property in z_result.Attributes.AttributeNames)
                        {
                            if (WantedAttributes.ContainsKey(z_property))
                            {
                                DirectoryAttribute z_details = a_result.Attributes[z_property];
                                if (z_details.Count == 1)
                                {
                                    // Special handling required for Attributes that aren't strings objectSid, objectGUID, etc
                                    string z_value = z_details[0].ToString().Trim();
                                    if (!string.IsNullOrWhiteSpace(z_value))
                                    {
                                        a_user.UserAttributes.Add(z_property, z_value);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
Robin Johnson
  • 357
  • 2
  • 11