Simplify configuration with a generic ConfigurationElementCollection class

I’ve been using Phil Haack‘s Custom Configuration Sections in 3 Easy Steps for some time now, and my configuration file management has never been happier… until I needed to read a collection of configuration elements.

Here’s a sample of the XML I needed to incorporate:

<importer>
	<filespecs>
		<add type="EmployeeCSV" path="d:\import\employees.csv" />
		<add type="OrderCSV" path="d:\import\orders_*.csv" />
	</filespecs>   
</importer>

The typical solution is to write your own collection class (FilespecConfigurationElementCollection), inheriting from ConfigurationElementCollection, and ensuring your Filespec object inherits from ConfigurationElement. I figured there has to be a better way — and there is.

I wrote a generic version of ConfigurationElementCollection, which you can use to avoid writing the custom collection class. The code that follows is the generic class, the Filespec object I used, and the property declaration from my ConfigurationSettings class (as described in Phil’s previously mentioned article).

Note one key part of this implementation: You must override the ToString() method of your custom ConfigurationElement class to return a unique value. The generic ConfigurationElementCollection uses the ToString() method to obtain a unique key for each element in the collection.

// The ConfigurationElementCollection<t> provides a simple generic implementation of ConfigurationElementCollection.
[ConfigurationCollection(typeof(ConfigurationElement))]
public class ConfigurationElementCollection</t><t> : ConfigurationElementCollection where T : ConfigurationElement, new()
{
	protected override ConfigurationElement CreateNewElement()
	{
		return new T();
	}
	protected override object GetElementKey(ConfigurationElement element)
	{
		return ((T)(element)).ToString();
	}
	public T this[int idx]
	{
		get { return (T)BaseGet(idx); }
	}
}
// The Filespec class is an example of a custom configuration element.
// Note that we inherit from ConfigurationElement, and use ConfigurationProperty attributes.
public class Filespec : ConfigurationElement
{
	public Filespec()
	{
	}
	[ConfigurationProperty("path", DefaultValue="", IsKey=true, IsRequired=true)]
	public string Path
	{
		get { return (string)(base["path"]); }
		set { base["path"] = value; }
	}
	[ConfigurationProperty("type", IsKey=false, IsRequired = true)]
	public FilespecType Type
	{
		get { return (FilespecType)(base["type"]); }
		set { base["type"] = value; }
	}

	public override string ToString()
	{
		return this.Path;
	}
}
// Finally, our ConfigurationSettings class (only part of the class is included).
// Note how we use the generic ConfigurationElementCollection.
public class ConfigurationSettings : ConfigurationSection
{
// ...
	[ConfigurationProperty("filespecs", IsRequired=true)]
	public ConfigurationElementCollection<filespec> FileSpecs
	{
		get { return (ConfigurationElementCollection</filespec><filespec>)this["filespecs"]; }
	}
// ...
}

If you’re using a number of ConfigurationElementCollections, this is a great way to simplify your code.

Performance tweaks and bug fixes to WilsonORWrapper

A minor update today (with one breaking change) to WilsonORWrapper:

  • BREAKING CHANGE: Renamed RetrieveTotalRecordCount() to GetObjectCount().
  • Implemented SQL2005 paging support by passing dummy where clause when none specified (they are required with SQL2005 paging); this is a hack, not a fix; you should always specify a sort clause when using any paging operation!
  • Cached value of EntityBase.IsReadOnly on first load; allowed dummy set operator to permit ObjectDataSource usage.
  • Improved IdentityBase.GetHashCode performance with single column keys.
  • Refactored to ensure mapping file stream is disposed.

Thanks to Michael Mepham for help in identifying and resolving these issues. The latest source code is available in ZIP format; no compiled binaries yet.

Wilson.ORMapper subscriber source code repository

Many users of Paul Wilson’s O/R mapper (“WORM”) know that there are a number of community-provided patches which are out there but not incorporated in any official release. Personally, I have maintained a copy of the source code in a private Subversion repository and incorporated a number of these patches for my own use. (Access to the full source code of WORM require you to pay a very reasonable $50 “subscriber fee” to Paul Wilson.)

I contacted Paul Wilson yesterday, asking him if he’d let me grant access to this repository to people who are WORM subscribers. Fortunately for all involved, he said yes!

What does this mean for you? If you are a subscriber to WORM, you can send me your full name and email address and request access to this private repository. I will confirm your subscription with Paul and grant you access.

The following changes have been incorporated into the aforementioned WORM instance:

The purpose of opening up this Subversion repository to subscribers is to give us all a place to share and contribute to a central copy of WORM that includes all the improvements and patches we maintain individually.

If you want to participate, send me your full name and email address.

A two-year-old’s first day at the movies

Last Sunday in New York was insanely hot, so my wife and I decided to take our two-year-old daughter to the movie theater for the first time.

We decided the safest bet was the Disney movie, Ratatouille, which was playing at 1:45PM in a nearby theater with stadium seating. It seemed a good fit — a kids movie and seats that’ll be high enough to let my daughter see over the seat in front of her without standing.

Who knew there was no such thing as a Sunday matinee? I didn’t, and I was more astounded that a movie ticket costs $10.25. Granted, it’s been a while since I’ve been to the movies, but ten bucks? I must be getting old. The consolation prize was that my daughter was free (no, I didn’t sneak her in).

We get a bag of popcorn (a small cost $5.50, a twice-the-size medium $6.00, no surprise there), and my wife insists on putting extra “butter” on it. I try to explain to her that it’s not butter. She knows, but she’s not one to be put off so easily. I grab an extra hundred napkins as a result.

We go into the movie theater, and my daughter is immediately apprehensive. It’s dark and LOUD — and the previews didn’t even start yet. We settle her down (giving her butter-flavored-stuff-covered-popcorn helps), and hope for the best.

When the previews get ready to start, things get even LOUDER (come on we’re not all deaf, does it have to be that loud?), and my daughter starts saying, ever so softly… “No… No…” As the sound gets louder, she gets louder. Certainly, she wasn’t quite ready for this.

I take her away from the blaring speakers and out into the hallway to give her a chance to settle down and get a little more acclimated with her surroundings. As soon as she’s out of the darkness, she falls back into her normal mode of operation — happy as a lark, running around, looking at everything, and talking to everyone.

Despite my best efforts to get her back into the theater (including some “come with daddy and do peek-a-boo with the mouse in the movie” [Yes, I know it’s a rat. Mouse sounds better to a 2-year-old.]) she’s unrelenting. Besides, she’s having too much fun playing in the corridors with daddy.

After thirty or so minutes, my wife comes out and tries taking her back into the theater. She had no more luck than I, and we realize it’s not the right day for her first movie. I figure it’s $20.50 down the tubes — until my wife asks if we can get our money back.

Incredibly, they say yes! Who would have thought that a movie theater would refund your money because your kid didn’t want to sit and watch a movie. I was flabbergasted and impressed at the same time — the former because they agreed to the refund, and the latter because someone has a clue what customer satisfaction means.

Anyway, so goes a day in the life. We’ll try the movies again in a few weeks — at least now we know it won’t cost us anything if we leave (fake-butter-covered-popcorn notwithstanding).

TableKit and default sort ascending

A recent post on the dexagogo newsgroup asked this question regarding TableKit, the JavaScript table enhancement:

… Is it possible, when you first click on a column to sort, to sort that column in ASC order instead of DESC like it now does?

Currently, when you click on a sortable column, the default sort order is descending (z-a). I would instead like to have it sort by ascending on first click (a-z).

Lucky for the guy who posted that message, I just recently modified a local copy of TableKit to do this. Around line 330, change this line:

order = order ? order : (cell.hasClassName(op.descendingClass) ? 1 : -1);

to this:

order = order ? order : (cell.hasClassName(op.descendingClass) ? 1 :
(cell.hasClassName(op.ascendingClassName) ? -1 : (cell.hasClassName('initialSortAsc') ? 1 : -1))
);

Then, in your HTML markup, add the class initialSortAsc to the column header, as in this:


...

Essentially the change does this:

  • If the cell has the descending class name, then sort ascending.
  • Otherwise, if it has the ascending class name, then sort descending.
  • Otherwise, if it has the class name initialSortAsc, then sort ascending.
  • Otherwise, sort descending.

In other words — by applying the class ‘initialSortAsc’ to your header row, it will make the default sort for that column ascending.

Improving TableKit’s sort performance in IE7

I noticed that TableKit‘s sort performance using IE7 grew progressively worse as tables grew in size (rows, not columns). I found the source of the problem and put in a simple fix to it. With the below change, sorting in IE7 is nearly as fast as in Firefox — that is, nearly instantaneous.

At line 322 (TableKit 1.2.1), comment the line as shown, and add the line noted.

        var tb = table.tBodies[0];
        var tkr = TableKit.Rows;
        rows.each(function(r,i) {
            tb.appendChild(r);
            //tkr.addStripeClass(table,r,i); /* THIS LINE COMMENTED */
        });
        TableKit.Rows.stripe(table); /* THIS LINE ADDED */

The change shouldn’t break anything as the new code added is standard TableKit code.

How to choose: Email, voice mail, or carrier pigeon?

Ayende posted how he has some 80+ unread and starred email messages waiting for him after a busy workweek. Reading this reminded me of a situation I ran into in the mid-1990s, while working in the IT group for a multinational bank. I went away for a little over a week, and I came back to find over 200 unread messages in my inbox. (This was before Blackberries and ubiquitous webmail.)

Reading through some 200 emails would have taken quite some time (even longer considering they used Lotus Notes), so I decided to take an alternate approach: I deleted them all.

Did I commit a grave error, deleting that all-important urgent email from a busy executive who needed to communicate something important to me? No, I did not. If something was that important, it would have been communicated to me via voice mail, or would have been redirected to one of my coworkers.

Granted, this may not be the best approach in all situations, but it illustrates some things we must recognize in a modern world.

  • Do not assume timely response to email. If something is urgent, you should always call someone. Never assume an email will be responded to in any timeframe, even if it has in the past: past performance does not predict future results.
  • Send a voice mail notice when sending important emails. If you have to send an email to communicate important information, call the person (leaving a voice mail message if necessary) notifying the person of the email.
  • Do not assume timely response to voice mail. Not everyone checks their voice mail regularly, so don’t assume your message will be heard any time soon.

Wait a minute — if we can’t rely on voice mail or email for timely communication, what can we do? Nothing, really. There will never be a convenient, reliable replacement for direct person-to-person communication. In my own life, I am trying to reduce my reliance on email and go back to traditional forms of communication (i.e. the spoken word, from my mouth to your ears). Email is convenient, but direct interpersonal communication (even if over the wire) is much more rewarding, interactive, interesting, and effective.

And remember… some people may seem to always be connected to the grid — via their Blackberry, Treo, web mail, text messages, mobile phone, or whatever. Just remember: batteries die, networks go down, service is not available everywhere, people forget to carry devices with them…

New York State highways: underperforming, but safe

imageA report released by the Reason Foundation, Performance of State Highway Systems, 1984-2005, 16th Annual Report, reveals two realities of New York State’s highway system, which you can see for yourself on their interactive map:

  1. It is highly inefficient.
  2. It is safe.

Anyone who lives in New Your State knows how bad our roads can be. In New York City (my home town), road surface anomalies come in many different styles (I never knew the proper name for a hummock until today).

Pothole Phil, courtesy of Staten Island AdvancePotholes in the big city have at times taken on lives of their own, and have created subcultures. Pothole Phil roams Staten Island, sticking his head in the many potholes he finds while smiling for the camera. Yes, this is the stuff of legends.

But what about the safety part? Why is it that New York State highways rank at the bottom in efficiency and quality, but at the top when it comes to safety (6th best in fatalities per 100 million miles)? It’s very simple, actually.

  1. Poor road conditions force drivers to drive slower to avoid a bone-rattling ride.
  2. High traffic conditions in New York force drivers to drive slower than the speed limit.
  3. You are less likely to get in a fatal accident when you’re driving slow.

Sometimes, the law of unintended consequences works out to some benefit.

Supporting Free Software: FileZilla

Last month, I announced that I’ll start donating $5 per month to a free and/or open source project. The first donation went to OpenOffice. This month, the donation goes to FileZilla, the free, open-source FTP client.

FileZilla is one of those utilities that you take for granted. It works so well that you tend to not think about it. There’s barely a day that goes by when I don’t fire up FileZilla at least once. Granted, there are plenty of FTP clients out there, and I’ve used many, but none satisfied me as much as FileZilla.

Thanks to Tim Kosse for starting and maintaining FileZilla over the years.