Monthly Archives: August 2009

KeyedCollection<TKey, TItem>

Neatly tucked away in the System.Collections.ObjectModel namespace is the KeyedCollection<TKey, TItem> class. This is a very cool abstract class which gives you both built-in indexing and dictionary access. All you have to do is override the relevant methods and you have your own custom collection.

The key thing (pardon the pun) to understand with this class is that the key must be a member of its corresponding item. I will clarify that with an example. In this example, I am going to create a class called IncrementAspxCollection which inherits from the KeyedCollection abstract class. The basic idea for this class will be that it will track users’ movements when they visit a web site.

First, I will create a class which will represent a page-visit by a user:

    public class PageVisit
    {
        private string page;
        private DateTime timeOfVisit;

        public string Page
        {
            get { return page; }
            set { page = value; }
        }

        public DateTime TimeOfVisit
        {
            get { return timeOfVisit; }
            set { timeOfVisit = value; }
        }

        public PageVisit(string page, DateTime timeOfVisit)
        {
            this.page = page;
            this.timeOfVisit = timeOfVisit;
        }

        public override string ToString()
        {
            return timeOfVisit.Hour.ToString() ;
        }
    }

Now, my IncrementAspxCollection class will contain objects of the PageVisit type. So, the obvious question is, if the key must be a member of its corresponding item, how will the collection know which member of that item to use as the key? This is easily done by overriding the KeyedCollection’s GetKeyForItem method:

        protected override string GetKeyForItem(PageVisit item)
        {
            return item.Page;
        }

There, I made the Page property of the PageVisit class the key for my new custom collection. I am now free to override the protected ClearItems, InsertItem, RemoveItem, and SetItem methods of the abstract KeyedCollection class.

For my limited purposes, I only overrode the InsertItem method. In doing so, I wanted to create a method which would add pages, to the collection as users load them. I also wanted to add numbering to the pages, so that if a user went back to a page during the same session, its number would increment. For example, if the user loaded default.aspx, the first entry in the collection would be default1.aspx. If that page was loaded again in the same session, it’s key would be default2.aspx etc. Implementing this was quite a lot of fun, in that it gave me an opportunity to use regular expressions; a very powerful string parsing technique.

        protected override void InsertItem(int index, PageVisit item)
        {
            int count = 1;
            Match m2 = Regex.Match(item.Page, @"(?<first>\D+)(?<second>\d*)\.aspx");
            string pageNameSansExt = m2.Groups["first"].ToString();

            //  Check to see if this is not the first item to be inserted.
            if (base.Dictionary != null)
            {
                IDictionary collDict = base.Dictionary;
                IEnumerator enumTor = collDict.Keys.GetEnumerator();
                while (enumTor.MoveNext())
                {
                    Match m1 = Regex.Match(enumTor.Current, @"(?<first>\w+)(?<second>\d+)\.aspx");
                    string origPageStr = m1.Groups["first"].ToString();

                    if (origPageStr.CompareTo(pageNameSansExt) == 0)
                    {
                        count++;
                    }
                }
            }

            //  If a page has already been inserted into the collection, increment the number
            //  appended to its name.
            if (count > 1)
            {
                item.Page = string.Concat(pageNameSansExt, string.Concat(count.ToString(), ".aspx"));
                base.InsertItem(index, item);
            }
            //  If this is the first instance of the page, append '1' to its name.
            else
            {
                item.Page = string.Concat(pageNameSansExt, "1.aspx");
                base.InsertItem(index, item);
            }
        }

I recently tested this out on a Web application that I was developing and the log file looked like this:

itwjaf45l35era55oqrfxt55, default1.aspx, 27/08/2009 12:10:12 AM
itwjaf45l35era55oqrfxt55, contact1.aspx, 27/08/2009 12:10:14 AM
itwjaf45l35era55oqrfxt55, contact2.aspx, 27/08/2009 12:10:15 AM
itwjaf45l35era55oqrfxt55, contact3.aspx, 27/08/2009 12:10:15 AM
itwjaf45l35era55oqrfxt55, default2.aspx, 27/08/2009 12:10:16 AM
itwjaf45l35era55oqrfxt55, default3.aspx, 27/08/2009 12:10:16 AM
itwjaf45l35era55oqrfxt55, default4.aspx, 27/08/2009 12:10:17 AM
itwjaf45l35era55oqrfxt55, default5.aspx, 27/08/2009 12:10:17 AM

The long, random string at the start of each line was the session ID for the user loading the pages.

So there we have a groovy, handy custom collection that fit my needs perfectly. And it was easily put together using the existing functionality (plus a little custom finesse) of a very cool abstract class. Feel free to download and play with my IncrementAspxCollection class.

Capitalizing the First Letter of a String

A good knowledge of the .NET framework can be a real time saver. Consider working with strings. It is obviously mandatory that programmers need to know the String class inside out. But there are other classes that can really save time when it comes to working with strings.

To demostrate this idea, consider the scenario where we want to capitalise the first letter of a word. One approach, using the String class, would be:

string.Concat(word[0].ToString().ToUpper(), word.Substring(1));

Now, whilst we have got the job done in 1 line of code, there are 4 method calls required. And we also had to think about it a little to bring it together. However, a programmer familiar with the CultureInfo class would not even have to think about it. They would just use the following line of code:

System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(word);

That’s one method call, with no calories burned in thinking through an approach to concatenate substrings etc.
Note, you need to add the following “using” directive to access the CultureInfo class – using System.Globalization;

So check out some of those handy classes available in the System.Globalization namespace. They often contain methods which produce exactly what you want, without you needing to re-create their functionality.

xQuery

I see xQuery as XSLT’s less popular, XML-derived brother. It has not been embraced to the same extent as XSLT, and does not get talked about as much on the Web. Where there is a void, there is opportunity; an opportunity to write about xQuery! So, I plan to write the odd post about xQuery, with demonstrations and observations that may prove valuable to someone learning this very useful and powerful XML query language.

The first thing to note is that you really need to know the rules of XML (the basics) to write xQuery queries.

The second thing to note is that you need to learn XPath before you learn xQuery. XPath is used throughout xQuery queries. Whilst a very useful tool for returning information about an XML document, XPath has its limitations and does not have the same transformational cababilities of xQuery.

xQuery is a functional language. Being the first functional language which I learnt, it took a bit of time to learn how to think in a “functional language” kind of way. Previously, I only knew object oriented/procedural programming paradigms.

So, on with a demonstration! For this demo, I am using the following XML file from Priscilla Walmsley’s excellent book, “XQuery”:

<catalog>
  <product dept="WMN">
    <number>557</number>
    <name language="en">Fleece Pullover</name>
    <colorChoices>navy black</colorChoices>
  </product>
  <product dept="ACC">
    <number>563</number>
    <name language="en">Floppy Sun Hat</name>
  </product>
  <product dept="ACC">
    <number>443</number>
    <name language="en">Deluxe Travel Bag</name>
  </product>
  <product dept="MEN">
    <number>784</number>
    <name language="en">Cotton Dress Shirt</name>
    <colorChoices>white gray</colorChoices>
    <desc>Our <i>favorite</i> shirt!</desc>
  </product>
</catalog>

Now, we can use xQuery, not just to return information about each product, but to also transform the results into an html file which can be displayed. To that end, I ran the following query, using the open source xQuery processor Saxon-HE:

declare option saxon:output "doctype-public=-//W3C//DTD XHTML 1.0 Transitional//EN";
declare option saxon:output "doctype-system=http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";

let $catalog := doc("catalog.xml")/catalog
return
	<html>
		<h1>Product Catalog</h1>
		<table style="border: 1px solid #C0C0C0; border-collapse: collapse;">
		<tr style="background-color: #C0C0C0;" >
			<td style="border: 1px solid #C0C0C0"><b>Product Nr</b></td>
			<td style="border: 1px solid #C0C0C0"><b>Description</b></td>
			<td style="border: 1px solid #C0C0C0"><b>Colours</b></td>
		</tr>
		{
			for $product in $catalog/product
				return

				<tr>
					<td style="border: 1px solid #C0C0C0">{$product/number/text()}</td>
					<td style="border: 1px solid #C0C0C0">{$product/name/text()}</td>
					<td style="border: 1px solid #C0C0C0">{$product/colorChoices/text()}</td>
				</tr>
		}
		</table>
	</html>

The output file, specified as an html document using Saxon at the command line, can be seen in this screen capture:

Screen shot of result as grid

Screen shot of result as grid


How cool is that?! It may not be all the rage, but I think xQuery is a very useful tool when working with XML documents, whether the documents are being used to store data, or to facilitate information exchange.

MCTS – Half Way There

Yesterday I passed the Microsoft 70-536 exam (Microsoft .NET Framework – Application Development Foundation)! So now, I am half way to an MCTS certification.
For the benefit of those curious about the exam itself, here are a few details:

  • there were 40 questions.
  • a time limit of 2 hours.
  • the pass mark was 700 (out of 1000) .
  • there was a feature whereby you could mark questions for review, using a checkbox. I found this really useful, as it enabled me to hone straight in on the ones which I had marked (as requiring further attention), once I had made my first pass through the exam.

I used a measureup test as part of my exam preparation, and I would like to pass comment on that. The measureup test was probably about the same level of difficulty as the exam itself. However, I thought that it was quite different. It just seemed different in the focus of the questions and how it approached the examinable content, coming at it from a different angle.

The one thing that the measureup test excelled in, was getting me to think about the content. It was not just straight memory recall. Many of the questions were quite involved, and you had to be able to look at a given situation, identify the issue and then recall the relevant content to apply to that situation. So there is definitely a large “application of knowledge” aspect to the measureup test, rather than straight recall. The exam itself also calls for this. But as I mentioned above, it did come at you from a slightly different angle than the measureup tests. 

One final tip. Normally when I take multiple choice exams, I try and find the correct answer straight away. With these exams, the choices are often very similar to one another. So, instead of attempting to find the correct answer straight off, I tried to eliminate ones which were incorrect. You can usually eliminate 2 choices, with a bit of effort (but belief me, you have to be quite meticulous). Then you are usually left with 2 or 3 choices (depending on the number of choices for the relevant question). With some questions, it really was an agonising grind to whittle that down to my final choice. Without the benefit of intellisense, recalling the various constructors, method overloads etc. for different classes can be quite a drain.

All in all, I’m delighted to have made it through, and look forward to finishing the job in the near future.

Machine Key Generator

I have been playing around with the security classes lately, and decided to make a useful little utility.

ASP.NET programmers will be familiar with Machine Keys and the machineKey element in the Web.config file. This is used for things like hashing passwords. The validationKey attribute can be from 40 – 128 characters long. The decryptionKey can be either 16 or 48 characters long. In Matthew MacDonald’s excellent book “Pro ASP.NET 2.0 in C# 2005”, he included a nice snippet of code that creates random validation and decryption keys for the machineKey element. I put a GUI front-end on that code, and voila, a nice handy utility that creates validation and decryption keys for the machineKey element:

Screen Shot of app

Machine Key Generator - Winforms App

If you would like to download it, click here. Just chuck the exe assembly in any directory you want for a nice, easy, no-touch installation. Once you generate the keys, just copy and paste them as required for your needs. Then click clear to clear the fields.