Performance Considerations for Applications Using Services
This topic discusses performance optimizations when using ASP.NET membership, roles, profile properties, session state, Web Parts personalization, and site navigation.
This section includes information about using ASP.NET membership efficiently.
Gather Membership Lists Efficiently
When calling methods of the Membership class, call the overloaded methods that take PageIndex and PageSize members. These overloads run faster because they result in less data being transferred from the Web server. An example is the GetAllUsers method of the Membership class. Calling the GetAllUsers method with no parameters returns all membership users. In contrast, calling the GetAllUsers overload returns only one page of users, reducing the quantity of data processed by the page.
Cache Results When Getting the Number of On-line Users
You can call the GetNumberOfUsersOnline method to display the number of users considered active on the Web site. This method examines each row in the membership table each time the method is called, which can be a significant performance penalty if the number of users in the database is large. Call the method only when necessary and cache the value for a period of time if you do not need an exact count.
By default, the GetRolesForUser method is called automatically each time a page loads, and returns the roles that a user is in. The roles are stored in a dictionary in the RolePrincipal object. While the Web page is running, checking for roles is performed using this dictionary. To prevent accessing the provider for each page load and thereby reduce server processing time, set the CacheRolesInCookie attribute in the application's Web.config file to true. This causes the list of the user's roles to be stored in a cookie. On subsequent page loads, role information can be read from the cookie instead of using a call to the provider.
The GetRolesForUser method, the IsInRole property, and the GetRoles method can be used in code to verify membership in a role. By understanding how these methods interact, you can write code for your application that works the most efficiently for the task you need to perform.
The GetRolesForUser method always accesses the provider. A call to the IsInRole method always results in a call to the GetRolesForUser method on the first request to a page when cookie caching is not enabled. If cookie caching is enabled, calls to the IsInRole method will instead use the role information cached in the cookie. The first call to the GetRoles method results in a call to the GetRolesForUser method whether cookie caching is turned on or not. Subsequent calls to the GetRoles method on a page though will use the role information cached inside of the RolePrincipal.
Storing sensitive information in a cookie can expose that information to users. For maximum security, do not enable cookie caching.
If your code accesses any profile property, when the page is loaded, the profile provider reads all profile properties for the current user. (Specifically, the provider reads all properties that are associated with that profile provider.) If any profile property values are changed, the new information is written back to the provider data store when the page unloads. ASP.NET can determine whether intrinsic types such as integers and strings have changed, but cannot determine whether non-intrinsic types have changed.
The algorithm for determining whether profile properties are saved is as follows:
If all profile property types are intrinsic types and none have changed, the profile is not written to the data store.
If all profile property types are intrinsic types and any have changed, all profile property values are written to the data store.
If any property is not an intrinsic type, ASP.NET cannot determine whether a value has changed. If the property is accessed in code, then the property value is written to the data store.
In all cases, the decision applies to all profile properties for a specific provider.
If you are using non-intrinsic property types, writing the profile data at the end of each page request takes time. You can mitigate this overhead in the following ways:
Use only intrinsic types in profiles.
Set the automaticSaveEnabled attribute in the <profile> configuration element to Off, and instead write custom code for detecting changes and saving property values when needed.
Write custom code to handle the ProfileAutoSaving event, and in the event code, determine whether any changes have occurred to the profile properties. If no properties have changed, cancel the automatic save operation and set the event's ContinueWithProfileAutoSave property to false.
Performance is a consideration when using any out-of-process session state mode (for more information, see Session-State Modes). This section provides information on optimizing performance of session state.
Reducing Session State Read and Write Overhead
By default, session state information is loaded during each page load. The EnableSessionState attribute in the @ Page directive gives you control over loading session state with the following settings:
True Session state data is read on every page load and saved if it has changed. ASP.NET can determine whether intrinsic types such integers and strings have changed. If all the session state values are intrinsic types and none have changed, the session is not saved. If any of the values is not an intrinsic type, and any non-intrinsic session value is accessed, then all the session state information is saved. This is because ASP.NET does not track whether non-intrinsic types have changed and makes the safest choice by saving all the data.
False Session state data is not read when the page is loaded.
ReadOnly Session state data is read on each page load but never saved, even if a change is made in code to session-state values.
Avoiding Lock Contentions for Session State
Avoid using <IFRAME> elements on pages that require session state. If multiple requests are made to ASP.NET with the same session-state identifier, and if the requests are all for ASP.NET pages where EnableSessionState is set to true, then the parallel requests will compete with each other for the lock that is associated with out-of-process session state. In the case of <IFRAME> elements, this means that ASP.NET pages displayed in multiple <IFRAME> elements on a page can potentially compete for the same session data. The result is that each separate request will be serialized on the server.
When a page runs, ASP.NET personalization information is loaded. To determine whether any Web Parts personalization information has changed, ASP.NET calls the Equals method to compare the old and new values of properties marked with the PersonalizableAttribute attribute.
If any personalization property value has changed, the personalization data is saved. For intrinsic types such as integers, the Equals method compares the old and new property values directly. However, for non-intrinsic types, ASP.NET compares the value of the reference, but not necessarily the data maintained by a type instance.
For example, for a property of type ArrayList, the Equals method compares the old and new values of the ArrayList reference, but not the contents of the ArrayList object. The Equals method would return true if the ArrayList reference had not changed, even if a new item had been added to the list. In that case, the ArrayList data would not be saved.
This algorithm for non-intrinsic types is efficient, but errs on the side of not saving the data. If you want to save the data of a non-intrinsic type such as an ArrayList object, set the IsDirty property to true if the control derives from WebPart, or call the SetPersonalizationDirty method that takes a control as a parameter.
Large site maps will slow performance compared to smaller site maps. For example, in testing scenarios increasing the number of nodes from 100 to 1000 (a ten-fold increase) can increase the page-load time by about a third.
Security trimming, which filters nodes based on roles, has a larger performance penalty than increasing the number of nodes. For example, a site map with 1000 nodes has 10 times as much processing overhead as a site map with 100 nodes.
Some recommendations for mitigating this effect are:
When security trimming is on, the recommended maximum number of nodes is 150.
Set the Roles attribute in each node of the site map. When this roles attribute exists, ASP.NET can bypass URL and file authorization for the URL that is associated with the SiteMapNode, as long as a user belongs to one of the roles that is listed in the attribute. However, note that if a user does not belong to any of the specified roles, then ASP.NET will fall back to the slower file and URL authorization checks.
Create a class that inherits from the SiteMapProvider class and override the IsAccessibleToUser method to check only the Roles attribute in each node of the site map. This speeds up the filtering process since it bypasses URL and file authorization. However, this approach requires two parallel security definitions in the Web application. The first is the authorization information in the Web.config file (and NTFS access control lists for file authorization if Windows authentication is enabled). The second is the role information in the sitemap. You should weigh the security management overhead of splitting the security information against the performance improvement in sitemap processing.