Dealing with GAC atrocities

Code reusability is one of the important pillars of Object Oriented programming (OOP). I like this feature a lot as It doesn't make sense to write a crucial piece of code again and again and in multiple places, which is actually being shared across applications. Why not to write it once and reuse it everywhere. Classes in OOP can be a very good example of this, which can be written once and used as many times as we want by initializing them as objects.

Anyone, who works with Visual Studio knows that referencing an assembly in the project paves the way towards code reusability. On the other hand, in order to achieve this same thing while in the production environment, we need to install assemblies in the Global Assembly Cache(GAC) . There are a lot of advantages of using GAC as a shared repository, but this post is not an effort towards that as there are a bunch of articles already available on the internet. What I want to share here are the concerns which I have listed below, which may come to minds before deciding on to go down this route of using GAC for our projects.

  1. Let's consider a scenario where we want to share a DLL say ExceptionHandler.dll between two applications HelloWorld and HelloUniverse. We have signed the assembly (DLL) using a strong name key and installed it in the GAC on the production server so that it would be utilized by both the applications. We have deployed the applications where they seem to be accessing the DLL from GAC. So far so good. Now assuming, the ClassLibrary project (ExceptionHandler.dll) is part of a solution which also contains either of the applications let's say HelloWorld, there is a possibility while building the application someone from the team can unintentionally do a FULL Debug of the solution, leaving the version number (build + revision) changed. Now, a week later our HelloUniverse application which is also pointing to the same DLL project undergoes a change and is deployed alone to the server assuming it will pick the same DLL that was deployed earlier and eventually he gets this:

    Could not load file or assembly 'ExceptionHandler, Version=3.1.65.66, Culture=neutral, PublicKeyToken=a2e77a5f9a0ce598' or one of its dependencies. The system cannot find the file specified.

  2. Let's assume for the time being that the situation above will never happen as there is no room for an unintentional behavior in your team. But, You will agree with me on the fact that using GAC as a shared repository motivates one towards having multiple versions of the same DLL as there is a native support for that. When it was designed to overcome problems like DLL HELL, then why not to use it. It is a very valid point to support applications still relying on the older version. One of the concerns is that if I come back to my GAC after a year, I will find it cluttered with multiple copies of the DLL with same name but with different version numbers.


  3. If the above scenario proves to be true, will it not become a manageability issue to keep a track of assembly versions on both production as well as development ends considering we have a reasonably big number of applications.

I am in no way trying to discourage anyone from using GAC, rather giving my point of view on the issues that get in our way whenever we decide to use it as a shared folder for assemblies. I may be wrong and may be these issues will never come for someone or they may not be a big concern for a few, but I believe sometimes it is good to be paranoid of implementing new technology or suggesting it over an existing one. This may give us an opportunity to think left, right and center before we get our hands dirty with it.

Before I finish, I would like to briefly comment on the way I believe above issues can be addressed. For the first point, each application or set of applications can point to their own copy of the shared DLL leaving no room for errors. The second and the third issues can be tackled by building applications targeting a single stable version of DLL at some point in future, which then can be replaced with all of the older DLLs with multiple versions in the GAC.

HTH,

Live Online SharePoint Saturday - EMEA (Free)

To all ya SharePoint ppl. based in EMEA region:

Live Online SharePoint Saturday - EMEA

HTH,

ASP.NET DropdownList with <optgroup>

If you are looking for an easy and simplest solution to create groups in the ASP.NET DropdownList control without writing unnecessary amount of backend code, then you are at the right place. Let me show you how we can do this using JQuery 1.2.6.

I have divided the solution into two different parts. First part explains achieving the desired output using JQuery's native support for wrapping elements into other elements with the help of wrapAll() method. It takes only 1 line of code and we can have the groups created in the HTML. However, the drawback of this approach is the groups will disappear on postback. But if the DropdownList is small and we are not loading it from the database, then this approach is very suitable as it will save a lot of time and effort.

Ok, enough of the riff-raff and get down to business now.

Part 01 (static binding of list items with no support for postback)

Step 01: Creating a DropdownList

ListItemCollection list = new ListItemCollection();

list.Add(new ListItem("1", "1"));
list.Add(new ListItem("2", "2"));
list.Add(new ListItem("3", "3"));
list.Add(new ListItem("4", "4"));
list.Add(new ListItem("5", "5"));
list.Add(new ListItem("6", "6"));
list.Add(new ListItem("7", "7"));
list.Add(new ListItem("8", "8"));
list.Add(new ListItem("9", "9"));
list.Add(new ListItem("10", "10"));

ddl.DataSource = list;
ddl.DataBind();

Step 02: Creating a new attribute
We have to create a new attribute for every DropdownList item. This new attribute will be used for grouping in the JQuery code.

protected void ddl_DataBound(object sender, EventArgs e)
{
foreach (ListItem item in ((DropDownList)sender).Items)
{
if (System.Int32.Parse(item.Value) < 5)
item.Attributes.Add("classification", "LessThanFive");
else
item.Attributes.Add("classification", "GreaterThanFive");
}

}

Step 03: The fun part - Creating groups using JQuery WrapAll() method

<script>
$(document).ready(function() {
//Create groups for dropdown list
$("select.listsmall option[@classification='LessThanFive']").wrapAll("<optgroup label='Less than five'>");
$("select.listsmall option[@classification='GreaterThanFive']").wrapAll("<optgroup label='Greater than five'>");
});

</script>


Part 02 (dynamic binding of list items with full support for postback)

This approach not only creates groups using JQuery 1.2.6 but also allows DropdownList to remember these groups across postbacks. We will have to create a custom server control using DropdownList class and override SaveViewState() and LoadViewState() methods that will help the page ViewState remember and restore groups respectively.

As mentioned earlier, in this approach we have to create a custom server control with a bit of a backend code and override some of it's important methods. However, if you are one of those lazy developers like me who don't like the idea of creating controls specially custom server controls that reside in a separate assembly for a very tiny job such as the one in hand, then you can create control that is part of the same assembly by adding a new class file and deriving it's class from System.Web.UI.WebControls.DropDownList.


namespace ControlLibrary
{
public class DropdownList : System.Web.UI.WebControls.DropDownList
{
protected override object SaveViewState()
{
// Create an object array with one element for the CheckBoxList's
// ViewState contents, and one element for each ListItem in skmCheckBoxList
object[] state = new object[this.Items.Count + 1];

object baseState = base.SaveViewState();
state[0] = baseState;

// Now, see if we even need to save the view state
bool itemHasAttributes = false;
for (int i = 0; i < this.Items.Count; i++)
{
if (this.Items[i].Attributes.Count > 0)
{
itemHasAttributes = true;

// Create an array of the item's Attribute's keys and values
object[] attribKV = new object[this.Items[i].Attributes.Count * 2];
int k = 0;
foreach (string key in this.Items[i].Attributes.Keys)
{
attribKV[k++] = key;
attribKV[k++] = this.Items[i].Attributes[key];
}

state[i + 1] = attribKV;
}
}

// return either baseState or state, depending on whether or not
// any ListItems had attributes
if (itemHasAttributes)
return state;
else
return baseState;

}

protected override void LoadViewState(object savedState)
{
if (savedState == null) return;

// see if savedState is an object or object array
if (savedState is object[])
{
// we have an array of items with attributes
object [] state = (object[]) savedState;
base.LoadViewState(state[0]); // load the base state

for (int i = 1; i < state.Length; i++)
{
if (state[i] != null)
{
// Load back in the attributes
object [] attribKV = (object[]) state[i];
for (int k = 0; k < attribKV.Length; k += 2)
this.Items[i-1].Attributes.Add(attribKV[k].ToString(),
attribKV[k+1].ToString());
}
}
}
else
// we have just the base state
base.LoadViewState(savedState);
}
}
}

If you search on the internet for a solution to create <optgroup> in ASP.NET DropdownList, you will find some of the articles talking about overriding RenderContents() method of DropdownList to create . However, I personally feel that this job can also be done with less of a hassle using JQuery WrapAll() method that wraps elements inside other elements. All we need is to have a mechanism to have these groups saved upon postbacks which we are doing here by overriding SaveVeiwState() and LoadViewState() as Scott Mitchell describes in his article. Apart from these 2 methods, we need to populate the DropdownList in Page_Load method and create a new attribute in the ddl_DataBound() event as explained in the part 01 above. That is all required to create <optgroup>s in an ASP.NET DropdownList. You can see the output control populated with ListItems which are properly encapsulated within optiongroups below.

The source code for this approach can be downloaded from here.

Output:




HTH,