<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Sitecore Xperiences &#187; Security Provider</title>
	<atom:link href="https://blog.peplau.com.br/category/security-provider/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.peplau.com.br</link>
	<description>The things I&#039;ve seen as a Sitecore Professional</description>
	<lastBuildDate>Sun, 09 Mar 2025 21:54:22 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.41</generator>
	<item>
		<title>Slowness with Custom Properties on .NET Security Provider</title>
		<link>https://blog.peplau.com.br/slowness-with-custom-properties-on-security-provider/</link>
		<comments>https://blog.peplau.com.br/slowness-with-custom-properties-on-security-provider/#comments</comments>
		<pubDate>Mon, 22 Aug 2016 00:49:52 +0000</pubDate>
		<dc:creator><![CDATA[Rodrigo Peplau]]></dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Security Provider]]></category>

		<guid isPermaLink="false">http://blog.peplau.com.br/?p=426</guid>
		<description><![CDATA[<div class="lr_horizontal_share" data-share-url="https://blog.peplau.com.br/slowness-with-custom-properties-on-security-provider/"></div>If you ever used the standard .NET Security Provider with Sitecore, you may love how easy it is to create and use Custom Profile Properties, where data can be easily saved at user profiles. But a huge issue can emerge if you attempt to retrieve users at your database by one of these custom properties. The Problem Let&#8217;s say you have a [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>If you ever used the standard .NET Security Provider with Sitecore, you may love how easy it is to create and use Custom Profile Properties, where data can be easily saved at user profiles. But a huge issue can emerge if you attempt to retrieve users at your database by one of these custom properties.</p>
<h2>The Problem</h2>
<p>Let&#8217;s say you have a custom property called <em>Document ID</em>, and you wish for some reason to retrieve the user that has a certain number on it &#8211; for instance, if your users can login to your website both using their <em>Logins</em> or <em>Document IDs</em> &#8211; then you may have something like this at your code:</p>
<pre>var userFound = UserManager.GetUsers().FirstOrDefault(f =&gt; f.Profile["Document ID"] == inputDocumentId);</pre>
<p>This simple code can bring you a big headache, because of the way .NET Security Provider builds the SQL Query responsible for executing your LINQ expression. Since all Custom Properties are stored as meta data, it is simply not directly queriable. Then what Security Provider does is one SELECT query <strong><span style="text-decoration: underline;">for each user</span></strong> at your database, deserializing the Custom Properties on memory so it can be compared to the user input.</p>
<p>If you have few users at your database, which is usually the case when you are in development phase, you&#8217;ll probably not notice any issue. But after your go-live, as your user base starts growing, it will gradually get slower and slower. At the project that inspired this blog post, we had a sudden data load of more than 20k users, and as a consequence the system got impracticably slow overnight.</p>
<h2>The Solution</h2>
<p>One of the possible technical solutions, the one we used at the project in question, was to extend the table <em>aspnet_Users</em> at our Core database. That is the main table used by .NET Security Provider to store users. What we do is to create a new column <em>Document ID</em>, where this data will be stored in a clean format:</p>
<p><a href="http://blog.peplau.com.br/wp-content/uploads/aspnet_Users.jpg"><img class="alignnone size-full wp-image-440" src="http://blog.peplau.com.br/wp-content/uploads/aspnet_Users.jpg" alt="aspnet_Users" width="316" height="183" /></a></p>
<p>After that, we need to attach some code to both &#8220;user:created&#8221; and &#8220;user:updated&#8221; pipelines. This code will be responsible for updating the Document ID column when users are created or updated.</p>
<pre>&lt;configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"&gt;
 &lt;sitecore&gt;
 &lt;events&gt;
 &lt;event name="user:created"&gt;
 &lt;handler type="MyProject.Pipelines.UpdateUserProfileCache, MyProject" method="OnUserCreatedUpdated" /&gt;
 &lt;/event&gt;
 &lt;event name="user:updated"&gt;
 &lt;handler type="MyProject.Pipelines.UpdateUserProfileCache, MyProject" method="OnUserCreatedUpdated" /&gt;
 &lt;/event&gt;
 &lt;/events&gt;
 &lt;/sitecore&gt;
&lt;/configuration&gt;</pre>
<p>Then your code will probably look like the following. The class <em>CustomProfileIndexManager</em>, responsible for doing the actual Update and Select at the database is not there, but you can easily guess how to build yours.</p>
<pre>namespace MyProject.Pipelines 
{
 public class UpdateUserProfileCache
 {
   public void OnUserCreatedUpdated(object sender, EventArgs args)
   {
     var scArgs = (SitecoreEventArgs)args;
     if (!scArgs.Parameters.Any())
       return;

     var user = (System.Web.Security.MembershipUser)scArgs.Parameters.First();

     // Will only act for "extranet" users
     var username = user.UserName;
     if (!username.StartsWith("extranet"))
       return;

     var scUser = Sitecore.Security.Accounts.User.FromName(username,false);

     // Following method is responsible for saving "Document ID" 
     // into the respective column at the database
     CustomProfileIndexManager.SaveUserToCache(scUser);
   }
 }
}
</pre>
<p>So your problematic code would be replaced by something like this:</p>
<pre>// Will do a simple "SELECT * FROM aspnet_Users WHERE Document ID = 'xxxx'" 
var userFound = CustomProfileIndexManager.GetUserByDocumentId(inputDocumentId);</pre>
<p>&nbsp;</p>
<h2>What about the existent base of users?</h2>
<p>Of course that will only cover users that are new or updated. If you already have a certain base of users, you can build a simple script to &#8220;touch&#8221; all your users, such as this:</p>
<pre> var startProcess = DateTime.Now;
 Response.Write("&lt;font color=\"red\"&gt;Loading users...&lt;/font&gt;");
 Response.Flush();

 // Get all users
 var users = DomainManager.GetDomain("extranet").GetUsers();
 Response.Write(string.Format("OK! ({0}ms)&lt;hr&gt;", DateTime.Now.Subtract(startProcess).TotalMilliseconds));
 Response.Flush();

 // Loop into all users
 var counter = 0;
 foreach (var user in users)
 {
   var startUserProcess = DateTime.Now;

   counter++;
   if ((counter % 10) == 0)
   {
     Response.Write(string.Format("--- Total time: {0} minutes&lt;br&gt;", DateTime.Now.Subtract(startProcess).TotalMinutes));
     Response.Flush();
   }
   Response.Write(string.Format("User #{0} - Email: {1} - Processing...", counter, user.Profile.Email));

   // Following method is responsible for saving "Document ID" 
   // into the respective column at the database
   CustomProfileIndexManager.SaveUserToCache(user);

   Response.Write(string.Format(" OK! ({0}ms)&lt;br/&gt;", DateTime.Now.Subtract(startUserProcess).TotalMilliseconds));
 }
 Response.Write(string.Format("&lt;h3&gt;TOTAL TIME: {0} minutes&lt;/h3&gt;", DateTime.Now.Subtract(startProcess).TotalMinutes));
 Response.Flush();
 Response.End();</pre>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.peplau.com.br/slowness-with-custom-properties-on-security-provider/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
