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
                };
            }
        }
7
  • Probably directly accessing the underlying DirectoryEntry. What does ConvertLdapUserPropertyToArray do? Also you could use UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, Username); instead of a PrincipalSearcher. Commented Jul 24, 2018 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. Commented Jul 24, 2018 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. Commented Jul 24, 2018 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. codeshare.io/21NDR0 Do you think this use will cause a slowdown? Commented Jul 24, 2018 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)" stackoverflow.com/questions/45357892 Sample implementation: codeshare.io/am3L0X Took 170 ms searching the user ~3k users 2.5 converting the props Commented Jul 24, 2018 at 19:56

1 Answer 1

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);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.