NunoGomesControlToolkit – Improving Web Apps performance

A few weeks ago I told you about a control toolkit I was making.

I decided to call him NunoGomesControlToolkit and is intended to improve web apps performance by decreasing total page size. This page size reduction is achieved by decreasing control ClientID size.

This control toolkit can be applied to any existing ASP.NET 2.0 Web Aplication by using the tagmapping configuration facility.

To obtain maximum redution it’s also recommended to extend webforms, masterpages and usercontrols not from regular Page, MasterPage and UserControl controls from ASP.NET framework but instead use the corresponding control from NunoGomesControlToolkit.

TagMapping is only used for markup interpretation and therefore all dynamic created controls are not mapped. To override this limitation its also included in this toolkit the DynamicControlBuilder class. Use this class to allow tagmapping over dynamic created controls.

As I promise, the control toolkit is now available at code.msdn.microsoft.com.[EDIT] Migrated to CodePlex.

I’m currently applying the NunoGomesControlToolkit to the BlogEngine.NET 1.3 version, and as soon as I test it I will make it available.

ASP.NET Controls – Improving automatic ID generation : The ShortIDs Naming Provider (Part 4)

In the previous posts on this subject I wrote about why automatic ID generation should be improved and how we can improve it. Now I will step forward and show you my own implementation of a specific naming provider.

As we saw in part 3, to create a specific Naming provider you only need to develop your own implementation of SetControlID method.

I named my naming provider ShortIDsProvider and it will have only one specification to meet:

    • it will create IDs in the form Txxx

where T denotes the ‘T’ character and xxx denotes an unique incremental integer value.[more]

Why using the ‘T’ character? Well, since Control.ID must always start by an alphabetic character … I choose on my own risk the ‘T’ char.

So … here it is:

 public   class   ShortIDsProvider  :  NamingProvider
 {
     #region  Private Fields
     private   const   string  ID_PREFIX =  "T" ;
     private   static   object  KeepLongIDsAttributeValueKey =  new   object ();
     #endregion  Private Fields

     #region  Public Methods
     private   bool  KeepOriginalID(System.Web.UI. Control  control)
    {
         bool  keepOriginalIDs =  false ;

         #region  KeepLongIDs Attribute Value Management
         if  (! this .KeepOriginalIDs)
        {
             if  ( HttpContext .Current.Items.Contains(KeepLongIDsAttributeValueKey))
            {
                keepOriginalIDs = ( bool ) HttpContext .Current.Items[KeepLongIDsAttributeValueKey];
            }
             else
             {
                 string  path = System.Web. HttpContext .Current.Request.Path.Replace(System.Web. HttpContext .Current.Request.ApplicationPath,  string .Empty);

                 if  ( this .ExceptionList !=  null  &&  this .ExceptionList.Contains(path))
                {
                    keepOriginalIDs =  true ; ;
                }
                 else
                 {
                     if  (control.Page !=  null )
                    {
                         object [] atts = control.Page.GetType().GetCustomAttributes( true );

                         foreach  ( Attribute  att  in  atts)
                        {
                             if  (att  is   KeepOriginalIDsAttribute )
                            {
                                keepOriginalIDs = (( KeepOriginalIDsAttribute )att).Enabled;
                                 break ;
                            }
                        }
                    }
                }
                 HttpContext .Current.Items[KeepLongIDsAttributeValueKey] = keepOriginalIDs;
            }
        }
         #endregion  KeepLongIDs Attribute Value Management

         return  keepOriginalIDs;
    }
     #endregion  Public Methods

     #region  NamingProvider Implementation

    /// <summary>
    /// Generates the Control ID.
    /// </summary>
    /// <param name="name">The controls name.</param>
    /// <param name="control">The control.</param>
    /// <returns></returns>
    public   override   string  SetControlID( string  name, System.Web.UI. Control  control)
    {
         if  ( this .KeepOriginalID(control))
        {
             return  name;
        }
         if  (control ==  null )
        {
             throw   new   ArgumentNullException ( "control" );
        }
         if  (control.NamingContainer ==  null )
        {
             return  name;
        }
         NamingContainerControlCollection  controlsCollection = control.NamingContainer.Controls  as   NamingContainerControlCollection ;
         if  (controlsCollection ==  null )
        {
             return  name;
        }

         string  shortid =  null ;
         if  (!controlsCollection.ContainsName(name))
        {
            shortid =  string .Format( "{0}{1}" , ID_PREFIX, controlsCollection.GetUniqueControlSufix());

             if  ( string .IsNullOrEmpty(name))
            {
                name = shortid;
            }
            controlsCollection.RegisterControl(shortid, name, control);
        }
         else
         {
            shortid = control.ID;
        }
         return  shortid;
    }

     #endregion  NamingProvider Implementation
}

As you can see it’s not rocket science, and enable us to create any automatic id generation strategy.

Naturally this ShortIDsProvider is only valuable in conjugation with a set of improved web controls.

Making an improved web control is also very simple and straight forward. Here is the improved TextBox control:

 public   class   TextBox  : System.Web.UI.WebControls. TextBox
 {
     #region  Naming Management

        /// <summary>
    /// Creates a new <see cref="T:System.Web.UI.ControlCollection"></see> object to hold the child controls (both literal and server) of the server control.
    /// </summary>
    /// <returns>
    /// A <see cref="T:System.Web.UI.ControlCollection"></see> object to contain the current server control's child server controls.
    /// </returns>
     protected   override   ControlCollection  CreateControlCollection()
    {
         return   NamingConfiguration .Provider.CreateControlCollection( this );
    }

    /// <summary>
    /// Gets or sets the programmatic identifier assigned to the server control.
    /// </summary>
    /// <value></value>
    /// <returns>The programmatic identifier assigned to the control.</returns>
     public   override   string  ID
    {
         get {  return   NamingConfiguration .Provider.GetControlID( this ,  base .ID); }
         set  {  base .ID =  NamingConfiguration .Provider.SetControlID( value ,  this ); }
    }

    /// <summary>
    /// Searches the current naming container for a server control with the specified id and an integer, specified in the pathOffset parameter, which aids in the search. You should not override this version of the <see cref="Overload:System.Web.UI.Control.FindControl"></see> method.
    /// </summary>
    /// <param name="id">The identifier for the control to be found.</param>
    /// <param name="pathOffset">The number of controls up the page control hierarchy needed to reach a naming container.</param>
    /// <returns>
    /// The specified control, or null if the specified control does not exist.
    /// </returns>
     protected   override   Control  FindControl( string  id,  int  pathOffset)
    {
         Control  ctrl =  base .FindControl(id, pathOffset);
         if  (ctrl ==  null )
        {
            ctrl =  NamingConfiguration .Provider.FindControl( this , id, pathOffset);
        }
         return  ctrl;
    }

    /// <summary>
    /// Raises the <see cref="E:System.Web.UI.Control.Init"></see> event.
    /// </summary>
    /// <param name="e">An <see cref="T:System.EventArgs"></see> object that contains the event data.</param>
     protected   override   void  OnInit( EventArgs  e)
    {
         this .EnsureID();
         this .ID =  base .ID;
         base .OnInit(e);
    }
     #endregion  Naming Management

}

The last step is configuration: first to instruct asp.net to use our improved web controls instead of the usual ASP.NET controls. We do this thru TagMapping configuration, like this:

<system.web>
  <pages>
    <tagMapping>
      <add tagType="System.Web.UI.WebControls.TextBox, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
           mappedTagType="IDsSample.Controls.TextBox"/>
    </tagMapping>
  </pages>
</system.web>

 

And second, setting the default Naming provider. The web.config should look similar to this one:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="NamingConvention" type="IDsSample.Configuration.NamingSection"
          allowDefinition="MachineToApplication"
          restartOnExternalChanges="true" />

  </configSections>
  <appSettings/>
  <connectionStrings/>
  <system.web>
    <compilation debug="true"/>
    <authentication mode="Windows"/>

    <pages>
      <tagMapping>
        <add tagType="System.Web.UI.WebControls.TextBox, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
             mappedTagType="IDsSample.Controls.TextBox"/>
      </tagMapping>
    </pages>
  </system.web>

  <NamingConvention defaultProvider="ShortIDs">
    <providers>
      <add name="ShortIDs" type="IDsSample.Providers.ShortIDsProvider"
           exceptionlist="/Exception.aspx"
           keeporiginalids="false"/>
    </providers>
  </NamingConvention>

</configuration>

Please note that TagMapping will only work for controls declared in markup. If you need to create dynamic controls then use the DynamicControlBuilder class.

I’m currently working on a NunoGomesControlToolkit that can be applied to any existing web application in order to increase performance by reducing the rendered html size.

While waiting for the NunoGomesControlToolkit you can try this sample solution: ShortIDs NamingProvider Sample