Thursday, December 18, 2014

Getting Display Names from User Names in a hostile SharePoint environment

I recently ran into a nasty situation where I needed a reliable way to get a list of user Full Names (or Display Names) from a list of usernames in a SharePoint process.

The short answer was easy...  The code runs server side so...

SPUser theUser = web.EnsureUser(username);
string DisplayName = theUser.Name;

//Right?

Well, under normal circumstances, sure.  

In this circumstance, I was checking a list of lists of user names, a condition where I might need to check hundreds of items, each of which could have a list of users to check.

No biggie, just add a lookup table and cache the results over multiple calls so that I only ever have to look a user up once in my process.

Now here's the real kicker.  In my target environment, EnsureUser comes back instantly if the username is a valid, active user in Active Directory.  If the user is not a valid user?   The command takes over 40 seconds per call to fail!

My solution was two-fold.  

1)  use the aforementioned cache strategy, which I have in my sample code below as _nameMap.
2)  Use a simple worker thread.  Give it two seconds to succeed.  Kill the thread if it takes longer than that for any reason.

I initially made the mistake of using SPContext.Current.Web in the thread, but that can *sometimes* produce a threading violation.   The code below creates a whole new instance of SPSite/SPWeb on every pass, but that's a lot safer and better performing than a lot of alternatives.


private Dictionary<string, string> _nameMap = new Dictionary<string, string>();  
        
private string GetUsersWithTempCacheAndTimeoutEnforcement(string rawUsers)
{
    string result = string.Empty;
    SPContext.Current.Web.AllowUnsafeUpdates = true;
    foreach (string aUser in rawUsers.Split(';'))
    {
        try
        {
            string addUser = string.Empty;
            string checkUser = aUser.Split('#')[1];
            if (checkUser.Contains("\\"))
            {
                lock (_nameMap)
                {
                    if (_nameMap.ContainsKey(checkUser))
                    {
                        addUser = _nameMap[checkUser] + "; ";
                    }
                    else
                    {
                        SPUser userResult = null;
                        SPContext context = SPContext.Current;
                        string webUrl = context.Web.Url;

                        System.Threading.ThreadStart st = new System.Threading.ThreadStart(
                            () =>
                            {
                                try
                                {
                                    using (SPSite site = new SPSite(webUrl))
                                    {
                                        using (SPWeb web = site.OpenWeb())
                                        {
                                            userResult = web.EnsureUser(checkUser);
                                        }
                                    }
                                }
                                catch (Exception)
                                { }
                            });
                        System.Threading.Thread workThread = new System.Threading.Thread(st);
                        workThread.Start();
                        workThread.Join(2000);
                        if (workThread.IsAlive)
                        {
                            workThread.Abort();

                        }
                        if (userResult == null)
                        {
                            _nameMap[checkUser] = checkUser;
                            addUser = checkUser + "; ";
                        }
                        else
                        {
                            _nameMap[checkUser] = userResult.Name;
                            addUser = userResult.Name + "; ";
                        }
                    }
                }
            }
            result += addUser;
        }
        catch (IndexOutOfRangeException)
        {
        }
        catch (Exception ex)
        {
        }
    }
    return result;
}