Sitecore API: Retrieving Active Directory Synced Users in a Controlled Way
Sitecore API becomes your best friend when it comes to development of big/medium Sitecore projects. So if you really care about flexibility and extensibility, you want to know the API in full and get the most of it. It's a friendly and pretty much straight forward library that gives you full access to your CMS environment from your custom developed code. But as always in software development, you want to pay attention to the way you develop in order to avoid performance issues. That being said, today I want to share with you how you can retrieve Sitecore's Users (i.e: Active Directory Synced Users) using the API in a way helps avoid potential timeouts issues.
Have you ever wanted to retrieve your Sitecore users at any custom sublayout developed by yourself? This is not something we used to do, but sometimes we get that requirement which needs some custom routine that works with our Sitecore's users. Let's continue with a case study and see how we used the API by following Sitecore best practices:
The client required a custom routine that would let them add "Articles" items to Sitecore from outside the content management panel. They also wanted to associate custom Read/Write/Modify permissions over every item created.
Can we create Sitecore Items using the Sitecore API? Of course we can! It's as simple as a line of code:
Can we retrieve Sitecore's users using the Sitecore API? Yes! The answer is right in the API method:
If we think about the above requirements, we would say it's as easy as cake! Based on the fact we have two lines of code that pretty much give us all we need, then it should not be an issue.
However, the truth is we have a couple of things to consider at this point that are actually major issues. Take a few minutes to see if you can figure out what the issues are before you continue reading. Did you figure it out?
We know that Sitecore CMS has Master and Web databases. While the Master contains all the content/assets and all their versions, the Web, on the other hand, contains only the latest version of any content item. This is the one used by your website instead of the Master. So what does this actually mean? Your custom layouts and sublayouts that are within the pages exposed to the end-users lives in the Web database, and therefore if you perform any creation or modification action over any of your "Context Items" you will be actually dealing with the content located at the Web database. This means those changes are potentially overridden by their Master database versions when any future publication from Master to Web database is performed. For read-only operations it will be always fine, but when we do custom operations, like being creating an item, we need to make sure we use the Master database.
The GetUsers() looks nice, but have you wondered how this method actually works? It basically performs a select operation over the database retrieving all the user's records, then it proceeds with some security checks over each retrieved user and at the same time, creates objects per each row and finally gives them back to the caller. Then the user can either do paging and/or filtering over the results and decide how to display them to the end-user. This doesn't look complex, but there is a performance concern when we deal with thousands of Synced Active Directory users which will give us, in the best case scenario: potential delays, worst case: application timeouts. So at this point, we would really like to have a way to do paging/filtering against the database instead of retrieving all the users and do it afterwards. How can we do that? well the answer is that we should use:
.GetPage(pageIndex, pageSize, filter).
Using the GetPage() method
When dealing with thousands of users, you should think about doing some custom paging and filtering against the database using the GetPage() method from Sitecore API. If you wondered, why is that method's call actually better based on the fact the GetPage() method is called right after GetUsers(), meaning it's going to page/filter after all the results are loaded into memory? What the above declaration actually does is that it tells the API that you do not want to load all the users into memory, only the ones following the paging attributes condition (pageIndex and pageSize) and the filter, if it exists. We just need to understand the parameters of that method:
pageIndex and pageSize: If you are familiar with paging routines this is going to be easy. But if not, then you want to always record of the Page Index you are at and the Page Size, in order to let the API retrieves the page you want at any stage.
filter: As expected, this parameter is optional. If you do not want to filter anything but instead just do some paging, that's fine. On the other hand, if you do want to filter, then you need to know that this parameter will be filtering only the UserName field and also, you wanna follow the wildcards characters specification while constructing the filter.
var Users = UserManager.GetUsers().GetPage(pageIndex, pageSize, "*john*");
The above call is going to retrieve all users that contain the word "john", of course being those users within the requested paging range.
In the end...
When we work with APIs we really need to know them as deeply as possible. This is because there are always limitations that we are not aware of that can harm our modules later on. Simple methods such as the "GetPage()" can save us from some painful performance issues during testing/production phases. Don't underestimate your task from the onset, you want to do a quick research and understand how those methods you are about to use actually works.