Saturday, December 20, 2008

Sorting with IComparable and Comparison Delegate on List<T>

One of the popular generic collection types in .Net 2.0 is List<T>, which we use to store a list of T items. For example, List<Country> stores a list of countries that defined by Country class. It works perfectly to bind the collection to a datasource to be displayed on, for instance drop down or grid view. But you may want the list to be sorted before it is bound to the datasource. Yes, List collection support a method called List<T>.sort() but in order to use sort() method, T must inherit IComparable interface.

To read more about this, visit here.

So let's take Country as the example. A typical Business Entity will look like:-

public partial class Country
{
public Country()
{
}

public Country(System.String countryName, System.Int32 id)
{
this.countryNameField = countryName;
this.idField = id;
}

private System.String countryNameField;

public System.String CountryName
{
get { return this.countryNameField; }
set { this.countryNameField = value; }
}

private System.Int32 idField;

public System.Int32 Id
{
get { return this.idField; }
set { this.idField = value; }
}
}


Next step is to inherit IComparable interface.



public partial class Country: IComparable<Country>



Once you have inherit the Interface, you are required to implement the definitions. In C#, you can right click on the Interface, and select Implement Interface > Implement Interface. Then the required method will be automatically added to your class. It will look similar to this:-



#region IComparable<Country> Members

public int CompareTo(Country other)
{
throw new Exception("The method or operation is not implemented.");
}

#endregion



Alright, you are almost done with a simple sorting. Now you need to implement the CompareTo method like this:-



#region IComparable<Country> Members

public int CompareTo(Country other)
{
return CountryName.CompareTo(other.CountryName);
}

#endregion


To test it, simply fill the List<Country> with data and call List<Country>.sort() method. It will automatically sort the list. Now is this what we need? If yes, move on to others. If no, what is the problem? Come to think of it, we may have different comparison in different scenario. For example, we may want to sort by ID in a GridView, and we may want to sort by Name (ASC or DESC) in a drop down box. So we want a more dynamic implementation.



Here, we need to use Generic Comparison delegate to provide different comparison methods. We may want to sort by ID or sort by Name in Descending order. Alright, you may simple need to write this:



#region Comparison Function

public static Comparison<Country> SortById = delegate(Country c1, Country c2)
{
return c1.Id.CompareTo(c2.Id);
};

// Sort by Name in DESC order
public static Comparison<Country> SortByName_DESC = delegate(Country c1, Country c2)
{
return c2.CountryName.CompareTo(c1.CountryName);
};

#endregion


So with Comparison delegates, you can come out with different kind of comparison to help you sort the list in your own way. The methods are being called in this way:



List<Country> list = new List<Country>();

list = GetCountries();

// Default Sorting
list.sort();

//Sort By ID
list.sort(Country.SortById);

//Sort By Name DESC
list.sort(Country.SortByName_DESC);



Done! If the above example does not help to solve your problem, you may want to consider creating a custom comparer class by inheriting from IComparer interface.



Cheers.

Monday, December 1, 2008

'Sys' is Undefined. Javascript error when you use AJAX extension

You probably will notice a scripting error when using ASP.Net AJAX Extension in your web pages.

Error: 'Sys' is undefined.

It is due to the ajax extension is not properly registered and configured in your web.config.

Solution:

There are a few things that you need to have in your web config.

1.) Make sure you have add below line to your assembly under <compilation><assemblies>

<add assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />



2.) Add following lines under <system.web>



<httpHandlers>
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
</httpHandlers>



3.) Add following line under <system.web><httpModules>



<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>



Cheers. This has solve my problem. I hope it does the same to you.



Some updates from Scott.



http://weblogs.asp.net/scottgu/archive/2006/10/29/tip_2f00_trick_3a00_-handling-errors-with-the-updatepanel-control-using-asp.net-ajax.aspx