Up to this point in the development of the WhatIWantMost project I have chosen to implement the simplest solution possible to complete each task (or at least that I could think of) and that will not end today. That approach is of course a bastardization of the one of core values of XP. To borrow a phrase from a colleague, whether you choose to swallow the Agile pill or not is entirely up to you, but some of the goals, beliefs and tenets just make sense. Simplicity, Communication and Feedback are all important. Accepting change just makes your life easier. And processes adopted and promoted by the Agile community like unit testing, acceptance testing, and continuous integration have found their place in more traditional development approaches as well.
Let’s get back to my current point, though. I have chosen the simplest approach to make it work. I chose to use .netTiers as a persistence mechanism and to serve as the core of the site. .netTiers and CodeSmith are not simple tools, but using them effectively is fairly simple for any developer that’s been around the block a time or two. I simply created a database model that would support the actors and functionality that I envisioned for the site, pressed a couple of buttons and my API was generated for me. To simplify my URLs and in many cases to point multiple URLs to one physical file I chose to implement another off-the-shelf tool, UrlRewritingNet.UrlRewrite. I chose to use the Amazon, Yahoo, Windows Live, and Ebay APIs to implement product and web searches.
Over the last few evenings I implemented a couple of the remaining items on my task list, adding the ability for users to invite others to view their wishlist and/or join the site, adding some caching for data frequently displayed on the site, and adding RSS feeds. I think I’ve probably shown enough examples to demonstrate how easy it is to work with .netTiers, but I know how much Dave Burke likes those examples so I will show one more. To create the support for the referral functionality I had to add a new table to my database that looks something like this:
Table: Referral
Fields:
PK ID int
FK UserID uniqueidentifier
DateCreated datetime
FirstName string
LastName string
EmailAddress string
DateJoined datetime
ReferralUserID uniqueidentifier
The DateJoined and the ReferralUserID field will be used to record if the invited user later becomes a member of the site. The UserID field records the id of the person creating the invite and the rest of the fields belong to person being invited and should be self explanatory. After creating that, running the .netTiers template on the database again and putting my newly created files in place all I had to do was create a simple web form and then add some code like this to handle those invitations:
Referral referral = new Referral();
referral.FirstName = txtFirstName.Text;
referral.LastName = txtLastName.Text;
referral.EmailAddress = txtEmail.Text;
referral.UserID = new Guid(CurrentUser.ProviderUserKey.ToString());
ReferralService.Insert(referral);
Email.SendReferral(referral.FirstName, referral.LastName,
referral.EmailAddress, CurrentUser.UserName,
txtMessage.Text);
Most of the above code makes use of the .netTiers generated API: I create a new referral, set the properties based on those filled into the form and then insert the referral into the database using the ReferralService.Insert() method. The final line of code makes use of an email utility class that I tend to add to most of my web projects to encapsulate all the email code. The email class is made up of mostly static methods and currently there are only two of them: Send(toaddress, fromaddress, subject, body) and the SendReferral(…) method shown above. The SendReferral method makes use of an email template, fills in the passed in data and then just calls the Send() method. All the email setup, retrieval of email server settings, and teardown are encapsulated in that utility class.
To add caching for Featured Items, Popular Items, New Members, Recently Updated Wishlists, etc. I added a controller class to mediate requests for those items from the .netTiers generated API. It firsts checks to see if those items are cached and if not then it requests them from the API. For caching I chose to keep it simple and use the System.Web.Caching.Cache object - there’s no need to create a wrapper or my own framework for the simple things I need done. I double-checked to make sure I was following Scott’s State Management tips and it looks like I’m compliant. Each of the items mentioned above are displayed on almost every page of the site. Even though what I’m pulling from the database for each of those is rather lightweight the performance gains after implementing the caching are noticeable.
Finally, to implement RSS I chose to use a combination of pointing all the “virtual” RSS feeds to one physical page using the Url rewriter and RSS.NET to write out the feeds. I currently only have a small number of feeds and I really don’t foresee needing much more so it made sense to handle them all in one place. Below are some example rss feed urls that all actually point to the file http://whatiwantmost.com/rss.aspx:
http://whatiwantmost.com/popular/Rss.aspx
http://whatiwantmost.com/hotgiftidea/Rss.aspx
http://whatiwantmost.com/recentmembers/Rss.aspx
http://whatiwantmost.com/wishlist/bobby/Rss.aspx
I chose to use RSS.NET because I’ve used it before and it works well. There’s no reason for me to create the code to write out my own RSS feeds when there is a tool available that does it. For the data to create the feeds I made use of the mediator class and the cached objects that I described above. The logic for displaying the feeds just needs to determine what feed it is supposed to be displaying, get the objects to display, and then use RSS.NET to display them. Following is example code showing getting the set of data to display (Popular Items in this case) and using the RSS.NET API:
RssChannel channel = new RssChannel();
List<PopularItem> items = Controller.PopularItems;
foreach (PopularItem item in items)
{
RssItem rssItem = new RssItem();
rssItem.Title = item.Itemname;
rssItem.Description = item.Description;
rssItem.Link = new Uri(UrlProvider.GetAmazonItemUrl(item.AmazonID));
rssItem.PubDate = (DateTime)item.DateAdded;
channel.Items.Add(rssItem);
}
channel.Title = "WhatIWantMost Popular Items";
channel.Description = "The most popular items on WhatIWantMost.";
channel.LastBuildDate = channel.Items.LatestPubDate();
RssFeed feed = new RssFeed();
feed.Channels.Add(channel);
Response.ContentType = "text/xml";
feed.Write(Response.OutputStream);
Response.End();
You can see in the code above that creating the RSS feed is a fairly simple process. I create the channel, get the data to display, loop through the data creating an RSSItem for each one, add each item to the channel, create an RSSFeed, add the channel to the feed and then write it all out to the page.
To date I have most of the original items crossed off my todo list other than reviews, some profile editing and printer friendly pages. Some new functionality has squeezed its way into the list since we started this project, but for now it will have to wait at the back of the line. I would guess that I am about 90% feature complete and about 75% complete with the first phase of the project. I’ve also realized that I still need to write some copy for pages in addition to things like the FAQ, Feedback forms, Privacy Policy, Terms and Conditions, etc. Right now the site is functional enough to put up for some feedback. In my next post I hope to announce that WhatIWantMost (Alpha) is live and available for you to poke at if you’d like.