Oct 3, 2008

Very elegant way to cut few more bytes from your ASP.NET markup and optimize your bandwidth a bit

I was thinking about how can I optimize the markup of a page a bit.
I found a great way (great but not very very very efficient) to do so.
It is based on a strategy that came in my mind recently.

Let's think a bit.
We already have very interesting methods to cut bandwith of an ASP.NET page like for example compressing the view state and so on and so on.
So provided we already have applied those techniques, if we still want to optimize what should we do?

I call my method : iterative profit, you will soon understand why ...

If we have something that repeats few times in the markup and is relatively long as name, if we make it shorter we should win some bytes right?
And if the page is big enough and "the thing" is repeated more times, we
will win more and more bytes.
So each time we cut the name of something, we win few bytes.

The next question is what can we cut in order to optimize our page?
What is the thing that repeats in each web control?

The answer is the "__doPostBack" function. I tried to replace it during the Rendering of the page and was able to cut a bit of the page size, while keeping the source relatively clean of errors (please note the word "relatively").

Here is a sample code:

     protected override void Render(HtmlTextWriter writer)
     {
        System.IO.
StringWriter stringWriter = new System.IO.StringWriter();
        
HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);
        
base.Render(htmlWriter);
        
string html = stringWriter.ToString();
        html = html.Replace(
"\"javascript:__doPostBack", "\"__dpb");
        writer.Write(html);
     }

I had some issues I haven't yet identified, as I was in a rush to post this as it seems to me a brand new way to optimize pages - to look for repeating elements and cut their names... (I will be happy if there are volunteers to help with this one ;)...


Other things : DoPostBackWithOptions should also be replaced by dpbw for example, this will be done in the future, after I clean the _doPostBack as it still have few problems.


And some more things - we may consider optimizing some of the ASP.NET controls which are known to be "markup unoptimized" like the treeview, gridview for example (At least I think there may be some things in them to optimize).

Any comments are welcome.

Something that a lot of C# developers will miss when designing their classes

The thing is that the developers often forget they can set access modificators at set / get level for a property.

Consider the following classes:

namespace Locals
{
  
class Program
   {
    
static void Main(string[] args)
     {
        
LocalManager LocalMgr = new LocalManager();
        Remote.
RemoteManager RemoteMgr = new Remote.RemoteManager();
        
User usr = new User();

        LocalMgr.Promote(usr);
        
Console.WriteLine("After local promotion: " + usr.Occupation);
     }
   }

  
public class LocalManager
   {
    
public LocalManager() { }
    
public void
style="color:Black;"> Promote(User usr)
     {
        usr.Occupation =
"Chief Officer";
     }
   }

  
public class User
   {
    
private string strOccupation;
    
public User() { }
    
public string Occupation
     {
        
get { return this.strOccupation; }
        
set { this.strOccupation = value; }
     }
   }
}

Here, after the program is executed the Local manager will promote the user and the output will be :
After local promotion: Chief Officer

Now imagine that we want to have the name of the manager who promoted the User, so we can store it in a database. Obviously we need to have an instance of the Manager class, and write the information in the database, prior changing the occupation for the user.

We will want to have a method in the User class, which takes the Manager instance, writes everything needed in the database and then changes the Occupation.
But we can't be sure that all developers will use the method and not the property directly.

Firs let's add a property to the LocalManager class to hold the name of the manager. Then I will create a method to change users occupation, which will take an instance of LocalManager. This will be the only way an external class to change the occupation of a user. After this, we will slightly modificate the code in the main program so everything is called properly. Here is the code:

using System;
using System.Collections.Generic;
using System.Text;

namespace Locals
{
  
class Program
   {
    
static void Main(string[] args)
     {
        
LocalManager mgr = new LocalManager();
        mgr.ManagerName =
"Pavel Donchev";
        
User usr = new User();
        mgr.Promote(usr);
        
Console.WriteLine("After promotion: " + usr.Occupation);
     }
   }

  
public class LocalManager
   {
    
private string strManagerName = string.Empty;

    
public LocalManager() { }
    
public void Promote(User usr)
     {
        usr.Promote(
this, "Chief Officer");
     }
    
public string ManagerName
     {
        
get { return this.strManagerName; }
        
set { this.strManagerName = value; }
     }
   }

  
public class User
   {
    
private string strOccupation;
    
public User() { }
    
public string Occupation
     {
        
get { return this.strOccupation; }
        
private set { this.strOccupation = value; }
     }
    
public void Promote(LocalManager manager, string Occupation)
     {
        
if (null != manager && !string.IsNullOrEmpty(manager.ManagerName))
        {
          
// Do additional work here, for example write the
           // name of the manager in the database amoung with
           // time stamp of the promotion to have audit...

          
this.Occupation = Occupation;
        }
        
else
        {
          
throw new Exception("A valid manager instance and occupation should be passed!");
        }
     }
   }
}

Now our classes are ready. You can give your user class away and be sure that no one can modify the property without passing a valid manager instance (actually the sequence of exceptions will guide your peers to follow the correct logic ;).


Oh, one more thing - we didn't proof that no one can assign the User.Occupation property directly. Well try to do so, you should receive compiler error message:

The property or indexer 'Locals.User.Occupation' cannot be used in this context because the set accessor is inaccessible