How can I efficiently grab users (with their properties) from a SharePoint web using CSOM? The code below results in multiple calls to the server (one for each user). It's ridiculously inefficient.
Also, is it possible to perform a Filter on the server?
public static List<Contact> GetUsers(Uri requestUri, string Filter = "")
{
ClientContext context;
var users = new List<Contact>();
if (ClientContextUtilities.TryResolveClientContext(requestUri, out context, null))
{
using (context)
{
var web = context.Web;
var peopleManager = new PeopleManager(context);
context.Load(web, w => w.Title, w => w.Description, w => w.SiteUsers);
var siteUsers = web.SiteUsers;
context.ExecuteQuery();
foreach (var user in siteUsers)
if (user.PrincipalType == Microsoft.SharePoint.Client.Utilities.PrincipalType.User)
if (user.Title.ToLower().Contains(Filter.ToLower()) && !users.Any(x => x.FullName == user.Title))
{
var userProfile = peopleManager.GetPropertiesFor(user.LoginName);
context.Load(userProfile);
context.ExecuteQuery();
var contact = new Contact() { FullName = user.Title, EmailAddress = user.Email };
if (userProfile.IsPropertyAvailable("Title"))
contact.Position = userProfile.Title;
if (userProfile.IsPropertyAvailable("UserProfileProperties") && userProfile.UserProfileProperties.ContainsKey("WorkPhone"))
contact.PhoneNumber = userProfile.UserProfileProperties["WorkPhone"];
users.Add(contact);
}
}
}
return users;
}
The SharePoint User Information List can be accessed via the browser by navigating to “/_catalogs/users/simple. aspx” from your site. You can see a screenshot of SharePoint Online user information list for on of my SharePoint Online site.
You can get the SharePoint Client Object Model URLs from the Microsoft site or can download the package from NuGet package. Open Visual Studio and create a new console application. Add CSOM (Client Side Object Model) Library from NuGet package. Replace the code in Program.
Major improvements
Since SharePoint CSOM supports Request Batching all the user profiles could be retrieved using a single request, for example instead of:
foreach (var user in siteUsers)
{
var userProfile = peopleManager.GetPropertiesFor(user.LoginName);
context.Load(userProfile);
context.ExecuteQuery();
}
user profiles could be retrieved as:
var userProfilesResult = new List<PersonProperties>(); //for storing user profiles
foreach (var user in siteUsers)
{
var userProfile = peopleManager.GetPropertiesFor(user.LoginName);
context.Load(userProfile);
userProfilesResult.Add(userProfile);
}
context.ExecuteQuery(); //submit a single request
Minor improvements
1)Since SharePoint CSOM supports CAML queries, the filtering operation could be performed on the server side, for example:
var siteUsers = from user in web.SiteUsers
where user.PrincipalType == Microsoft.SharePoint.Client.Utilities.PrincipalType.User
select user;
var usersResult = context.LoadQuery(siteUsers);
context.ExecuteQuery();
2)You could also use the following checking to determine whether user profile exist for a specified user:
var hasUserProfile = userProfile.ServerObjectIsNull != null && userProfile.ServerObjectIsNull.Value != true;
Modified example
public static List<Contact> GetUsers(Uri requestUri, ICredentials credentials, string filter = "")
{
ClientContext context;
var users = new List<Contact>();
if (ClientContextUtilities.TryResolveClientContext(requestUri, out context, credentials))
{
var userProfilesResult = new List<PersonProperties>();
using (context)
{
var web = context.Web;
var peopleManager = new PeopleManager(context);
var siteUsers = from user in web.SiteUsers
where user.PrincipalType == Microsoft.SharePoint.Client.Utilities.PrincipalType.User
select user;
var usersResult = context.LoadQuery(siteUsers);
context.ExecuteQuery();
foreach (var user in usersResult)
{
if (user.Title.ToLower().Contains(filter.ToLower()) && !users.Any(x => x.FullName == user.Title))
{
var userProfile = peopleManager.GetPropertiesFor(user.LoginName);
context.Load(userProfile);
userProfilesResult.Add(userProfile);
}
}
context.ExecuteQuery();
var result = from userProfile in userProfilesResult
where userProfile.ServerObjectIsNull != null && userProfile.ServerObjectIsNull.Value != true
select new Contact() {
FullName = userProfile.Title,
EmailAddress = userProfile.Email,
Position = userProfile.IsPropertyAvailable("Title") ? userProfile.Title : string.Empty,
PhoneNumber = userProfile.IsPropertyAvailable("UserProfileProperties") && userProfile.UserProfileProperties.ContainsKey("WorkPhone") ? userProfile.UserProfileProperties["WorkPhone"] : string.Empty
};
users = result.ToList();
}
}
return users;
}
When you are in a specific context of a website you can do followings:
using (var ctx = new ClientContext("http://theWebsite"))
{
var list = ctx.Web.SiteUserInfoList;
var users = list.GetItems(new CamlQuery());
ctx.Load(users);
ctx.ExecuteQuery();
// do what you want with the users
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With