Aaronontheweb

Hacking .NET and Startups

.NET Culture Shock: Why .NET Adoption Lags Among Startups

July 3, 2010 11:48 by Aaronontheweb in .NET, Startup // Tags: , , // Comments (108)

One of the other things I took away from Code Camp was a bit of .NET culture shock. As you can tell by glimpsing around on this blog, I am somewhat enamored with the idea of starting my own business. I’m a natural entrepreneur and it is my wont to think about startups constantly.

That being said, I’ve always wondered why a platform as widely adopted and supported as .NET isn’t more visible in startup culture. Many major open source  platforms and languages have very visible and vocal presences in the startup community, everything from mainstays like Python and PHP to even the more obscure and specialized ones like Clojure and Hadoop.

.NET on the other hand is conspicuously absent from the startup conversation despite the fact that it is a singularly larger platform than any of the others.

It’s like there’s a silent majority among the development community that just tinkers away on its own projects without even occasionally raising its hands and saying “here’s something clever and unique we did with this platform that no one else has done before.”

It’s somewhat tragic for the .NET community too – because the perceived lack of sex-appeal on the surface doesn’t match the reality of what the platform is capable of.

Just take a brief moment to peruse through CodePlex for more than a couple minutes and you’ll find thousands upon thousands of examples creative open source projects all built in .NET.

And in the startup space there actually are a number of .NET-based startups making it big, including this week’s Hacker News / Social Media darling Woot. But why oh why isn’t there a louder .NET voice in the startup community? Why aren’t there developers from Woot working with developers from StackOverFlow (also implemented in .NET) to encourage more startups to use the extensive .NET stack to create new and exciting products and services?

And most importantly, why aren’t there more startups adopting .NET?

It’s All about the Enterprise

I’ve heard all sorts of lame answers to this question before: “platform lock-in,” “no open standards,” “licensing costs,” and none of them pass the test of objective reality – yes, those are issues that might prevent some developers from adopting .NET in the startup space, but not enough to bar virtually all of them from using .NET, arguably the most comprehensive and versatile platform with the best tools and the best support.1

At Code Camp I think I finally figured out why this is: it’s the culture of the .NET community itself, not anything specific to the platform or the architecture which supports it.

.NET culture is centered around the concerns of  the enterprise – the large already-established businesses in the economy, not plucky up-and-coming startups. The Promotion PitAnd when I say “culture,” I’m not talking about the development tools – I’m talking about where the hearts and minds of the developers who use the .NET platform are. I’m talking about the blogs and media sources .NET developers read. I’m talking about the networks where .NET developers contribute and the substance of their conversations.

Most of the developers I met developed portals for giant healthcare providers with thousands of employees, worked on legacy code whose lifespan could be measured in decades, worked in teams with hundreds and even thousands of programmers, and lived in ecosystems ruled by large bureaucracies. These are problems to which few if any developers for startups can relate.

That’s why so many of the talks at Code Camp were staked around RAD methodologies for developing internal projects, coding standards, enterprise architecture, and other things that matter to developers who work in giant ecosystems.

And who can blame Microsoft for catering to the enterprise market – that’s where the money is! No one ever became rich selling high-end development tools to a handful of capital-starved, small companies.

However, the consequences of the .NET community’s enterprise-centric culture is that the startup community and the .NET community don’t overlap as much as they do for other technologies.

As a result, developers working for startups and even the startup founders themselves don’t get much exposure to .NET and don’t think of it as an applicable tool for their purposes.

The flipside is that many .NET developers who want to work for hot startups don’t have as many opportunities to do so unless they abandon the platform for a more “startup-friendly” one or start a company themselves.

So what do the members of the .NET community fuss over? They worry about supporting legacy systems, building enterprise architectures which can service multiple departments, they worry about large systems for supporting in-house business processes, and they worry about satisfying the needs of stakeholders throughout their companies.

.NET developers in general, worry about architecture in order to support systems.

Members of the startup community fuss over a very different set of issues – they’re their own stakeholders, they worry about concurrency, they worry about scalability, they worry about user experience design, they worry about supporting multiple clients and browsers, and they worry about how their designs will impact their bottom line.

Startup developers in general, worry about architecture in order to support products.

See the difference? .NET is perfectly capable of fulfilling the needs of product designers and startups, but comparatively little2 of the .NET culture, literature, and conversation is product-centric – instead the majority of it is structured around using .NET to support the needs of the business, not building the business’ products itself!

But It Doesn’t Have to Be

One thing that should be clear here is that any platform, not just .NET, can be used as a platform for building enterprise technology and for building new products. The platforms don’t often dictate their use – that’s the work of the people who develop on them.

If Microsoft wants to make a splash in the startup scene with .NET, and I have every reason to believe they do, then they need to do a better job evangelizing .NET’s capacity from a holistic new service / new product / new business perspective and not just for the enterprise.

Enterprise and startups aren’t mutually exclusive – they’re just different stages in the evolution of software, and there’s no reason why the startup community shouldn’t look at .NET as an attractive starting point for a new business.

The next article I’m going to write on this subject will cover why it’s a good idea for Microsoft to have .NET adopted more readily by the startup community.


1I realize “best” is a subjective term, but please do your best to just stick with me here. I’m not trying to trash Eclipse, XCode, NetBeans, or EMACS, but just face it: Visual Studio destroys those in any feature set-to-feature set comparison.

2Again, this is an observation based on my experience with the .NET community thus far. I'm always on the lookout for any exposure to people who use .NET in a startup setting.

If you enjoyed this post, make sure you subscribe to my RSS feed!



Announcing the Release of Quick and Dirty Feed Parser

June 30, 2010 18:47 by Aaronontheweb in .NET, QDFeedParser // Tags: , , , // Comments (0)

Alternate headline: "never see XML again."

Ok, that may be a bit of a stretch. Regardless, I'm quite pleased to announce the launch of Quick and Dirty Feed Parser, a library for people who want a seamless way to use Atom and RSS feeds in their .NET 2.0+ applications without having to deal with the XML.

"Does the world really need another RSS/Atom parser," you ask? I'll let my source code speak for itself:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QDFeedParser;

namespace QDFeedParserCMD
{
    class Program
    {
        private static readonly string DefaultFeedUri = "http://www.aaronstannard.com/syndication.axd";

        static void Main(string[] args)
        {
            try
            {
                string strFeedUri = args.Length == 0 ? DefaultFeedUri 
                    : args[0];

                Uri feeduri = new Uri(strFeedUri);
                IFeedFactory factory = new HttpFeedFactory();
                IFeed feed = factory.CreateFeed(feeduri);

                foreach(var feedItem in feed.Items)
                {
                    Console.WriteLine("{0} {1}", feedItem.DatePublished,
                        feedItem.Title);
                    Console.WriteLine(feedItem.Link);
                }
                //Just to prevent the window from instantly bailing out.
                Console.ReadKey(); 
            }
            catch(Exception ex)
            {
                Console.WriteLine("Exception: {0}", ex);
            }

        }
    }
}

That's about as complicated as it gets, folks. And feel free to download this QDFeedParser example from CodePlex

I wrote Quick and Dirty Feed Parser largely because I wanted to incorporate a really straightforward RSS/Atom parsing tool into a number of BlogEngine.NET modifications I have in mind in the near future, and I wanted a standard interface for dealing with RSS/Atom feeds that was available to me in .NET 2.0.

Quick and Dirty Feed Parser is my first OSS project so I would love your feedback, contributions, and most awesomely - examples of it actually being used! Check out the QDFeedParser project on CodePlex if you want to get involved!

QD Feed Parser in Action - The Great Wall of Geeks

No project worth it's salt would be complete without some sort of frivolous example, right? Well that's exactly why I created The Great Wall of Geeks.

Feel like your geeky blog belongs on the wall? Leave a comment below!

If you enjoyed this post, make sure you subscribe to my RSS feed!



How to Query a User's del.icio.us Feed with RestSharp

June 14, 2010 12:26 by Aaronontheweb in .NET, Del.icio.us // Tags: , , , , , // Comments (2)

I've been meaning to give RestSharp a go since I first started using Hammock in my startup project's codebase, just because I had heard some good things about RestSharp's auto-parsing capabilities.

This weekend I cobbled together a small example using del.icio.us' RSS feeds (not to be confused with its draconian REST API) for users and RestSharp performed magnificently, although its POCO -> XML Element mapping process requires a lot of experimentation before you get it just right.

Here's an example RSS feed for my personal del.icio.us account:

<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://web.resource.org/cc/">
  <channel>
    <title>Delicious/Aaronontheweb</title>
    <link>http://delicious.com/Aaronontheweb</link>
    <description>bookmarks posted by Aaronontheweb</description>
    <atom:link rel="self" type="application/rss+xml" href="http://feeds.delicious.com/v2/rss/Aaronontheweb?count=2"/>
    <item>
      <title>Windows Presentation Foundation (WPF) Dialog Boxes Overview</title>
      <pubDate>Fri, 04 Jun 2010 23:48:12 +0000</pubDate>
      <guid isPermaLink="false">[REMOVED]</guid>
      <link>http://msdn.microsoft.com/en-us/library/aa969773.aspx</link>
      <dc:creator><![CDATA[Aaronontheweb]]></dc:creator>
      <comments>[REMOVED]</comments>
      <wfw:commentRss>[REMOVED]</wfw:commentRss>
      <source url="http://feeds.delicious.com/v2/rss/Aaronontheweb">Aaronontheweb's bookmarks</source>
      <description>How to use dialog boxes in WPF (for WPF noobs like myself.)</description>
      <category domain="http://delicious.com/Aaronontheweb/">.net</category>
      <category domain="http://delicious.com/Aaronontheweb/">wpf</category>
      <category domain="http://delicious.com/Aaronontheweb/">dialogs</category>
      <category domain="http://delicious.com/Aaronontheweb/">tutorial</category>
      <category domain="http://delicious.com/Aaronontheweb/">nullable</category>
      <category domain="http://delicious.com/Aaronontheweb/">c#</category>
    </item>
    <item>
      <title>Developer's Guide: Data API Protocol - YouTube APIs and Tools - Google Code</title>
      <pubDate>Mon, 31 May 2010 22:43:47 +0000</pubDate>
      <guid isPermaLink="false">[REMOVED]</guid>
      <link>[REMOVED]</link>
      <dc:creator><![CDATA[Aaronontheweb]]></dc:creator>
      <comments>[REMOVED]</comments>
      <wfw:commentRss>[REMOVED]</wfw:commentRss>
      <source url="http://feeds.delicious.com/v2/rss/Aaronontheweb">Aaronontheweb's bookmarks</source>
      <description>Finally figured it out - how to sign all requests with my API key using a querystring. So simple, yet so difficult.</description>
      <category domain="http://delicious.com/Aaronontheweb/">youtube</category>
      <category domain="http://delicious.com/Aaronontheweb/">API</category>
      <category domain="http://delicious.com/Aaronontheweb/">key</category>
      <category domain="http://delicious.com/Aaronontheweb/">Google</category>
      <category domain="http://delicious.com/Aaronontheweb/">gdata</category>
    </item>
  </channel>
</rss>

The best way to utilize RestSharp to parse any sort of API response, whether it's custom REST XML, JSON, Atom, or RSS, is to first take a look at the raw response format and then to try to model a set of Data Transfer Objects (DTOs) which contain the response format elements you want to actually use in your application. Your DTOs just need to be simple POCO objects for this to work, but there are a set of POCO-XML Element matching rules you need to observe if you're using RestSharp's default deserializer - you can read them here.

Here are the classes that I modeled for this RSS format:

public class RssFeed
{
    public string Version { get; set; }
    public RssChannel Channel { get;set; }
}

public class RssChannel
{
    public string Title { get; set; }
    public string Description { get; set; }
    public string Link { get; set; }
    public RssItems Item { get; set; }
}

public class RssItems : List<item>{}

public class item
{
    public string Title { get; set; }
    public string Description { get; set; }
    public string Link { get; set; }
    public string Author { get; set; }
    public string Comments { get; set; }
    public string PubDate { get; set; }
}

This diagram explains how these DTO classes map to the del.icio.us RSS format:

del.icio.us restsharp POCO class to XML document mapping

ProTip: Handling Lists of XML Elements with POCO Classes

One thing that's a bit tricky is handling lists of XML elements, such as the <item> elements in this case. In order for RestSharp to deserialize them, you need create a List<T> object where the name of class T matches the name of the listed element, which is why my class name is item in this case.

Once you have your POCO classes in order, then you need to actually make requests against the del.icio.us feed for a particular user. Here's my code for doing that:

public class DeliciousRequest
{
    public const string DeliciousFeedBase = @"http://feeds.delicious.com/v2/rss/";

    private RestSharp.RestClient _client;

    public DeliciousRequest()
    {
        this._client = new RestClient
                          {
                              BaseUrl = DeliciousFeedBase
                          };
    }

    public RssFeed GetBookMarksForUser(string username)
    {
        var request = new RestRequest {RequestFormat = DataFormat.Xml, Resource = username};
        var response = this._client.Execute<RssFeed>(request);
        return response.Data;
    }

}

All RSS feeds for del.icio.us users can be found using http://feeds.delicious.com/v2/rss/{USERNAME}, therefore you can understand why the RestClient's BaseURL and the RestRequest's Resource arguments are defined as they are. Once you have your RestRequest defined, you simply call the client's Execute<T> method where T is the type of your POCO DTO class.

And that's it - RestSharp is easy, and I look forward to creating some more examples with it down the road.

More RestSharp Examples:

If you enjoyed this post, make sure you subscribe to my RSS feed!



Discussion: How to Use RestSharp / Hammock to Automatically Parse the YouTube Response Format into POCO Objects

June 8, 2010 17:35 by Aaronontheweb in .NET, YouTube // Tags: , , , , , // Comments (3)

If you've been following me on Twitter over the past couple of weeks, you might have noticed that I've been a little frustrated with the YouTube GData API lately. Simply put: XML makes me sad. Since that frustrated Tweet I've developed a solution using LINQ-to-XML and a bunch of hard-coded namespaces which isn't how I would prefer to do it.

I would much rather use the built in object deserialization capabilities in RestSharp or HammockREST. I'll be honest - I do not have a damn clue how to use Hammock's built-in deserialization capabilities. I tried tinkering with it on my own to no avail, and there's not much documentation to speak of.

RestSharp has some more detailed documentation on its deserialization capabilities, but it doesn't answer some lingering questions I have. So without further aideu, I'd like to solicit the opinion of the developer community.

<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:yt="http://gdata.youtube.com/schemas/2007" gd:etag="W/&quot;DkMMRn0zeSp7ImA9WxFWE0k.&quot;">
  <id>tag:youtube.com,2008:user:smartdraw:uploads</id>
  <updated>2010-05-31T22:21:27.381Z</updated>
  <category scheme="http://schemas.google.com/g/2005#kind" term="http://gdata.youtube.com/schemas/2007#video" />
  <title>Uploads by smartdraw</title>
  <logo>http://www.youtube.com/img/pic_youtubelogo_123x63.gif</logo>
  <link rel="related" type="application/atom+xml" href="http://gdata.youtube.com/feeds/api/users/smartdraw?v=2" />
  <link rel="alternate" type="text/html" href="http://www.youtube.com/profile_videos?user=smartdraw" />
  <link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://gdata.youtube.com/feeds/api/users/smartdraw/uploads?v=2" />
  <link rel="http://schemas.google.com/g/2005#batch" type="application/atom+xml" href="http://gdata.youtube.com/feeds/api/users/smartdraw/uploads/batch?v=2" />
  <link rel="self" type="application/atom+xml" href="http://gdata.youtube.com/feeds/api/users/smartdraw/uploads?start-index=1&amp;max-results=25&amp;v=2" />
  <link rel="service" type="application/atomsvc+xml" href="http://gdata.youtube.com/feeds/api/users/smartdraw/uploads?alt=atom-service&amp;v=2" />
  <link rel="next" type="application/atom+xml" href="http://gdata.youtube.com/feeds/api/users/smartdraw/uploads?start-index=26&amp;max-results=25&amp;v=2" />
  <author>
    <name>smartdraw</name>
    <uri>http://gdata.youtube.com/feeds/api/users/smartdraw</uri>
  </author>
  <generator version="2.0" uri="http://gdata.youtube.com/">YouTube data API</generator>
  <openSearch:totalResults>30</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
  <entry gd:etag="W/&quot;CEIHQn47eCp7ImA9WxFWE0k.&quot;">
    <id>tag:youtube.com,2008:video:NJPrllhYZrg</id>
    <published>2010-02-10T23:27:38.000Z</published>
    <updated>2010-05-31T21:48:53.000Z</updated>
    <category scheme="http://schemas.google.com/g/2005#kind" term="http://gdata.youtube.com/schemas/2007#video" />
    <category scheme="http://gdata.youtube.com/schemas/2007/categories.cat" term="People" label="People &amp; Blogs" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="SmartDraw" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="flowcharts" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="visuals" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="communicate" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="visually" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="communication" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="powerpoint" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="presentations" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="mind" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="maps" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="software" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="Business" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="graphics" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="strategic" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="planning" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="solutions" />
    <category scheme="http://gdata.youtube.com/schemas/2007/keywords.cat" term="tools" />
    <title>SmartDraw 2010 Guided Tour</title>
    <content type="application/x-shockwave-flash" src="http://www.youtube.com/v/NJPrllhYZrg?f=user_uploads&amp;d=AWaEdOkfU7AZas-hLyE9s8EO88HsQjpE1a8d1GxQnGDm&amp;app=youtube_gdata" />
    <link rel="alternate" type="text/html" href="http://www.youtube.com/watch?v=NJPrllhYZrg&amp;feature=youtube_gdata" />
    <link rel="http://gdata.youtube.com/schemas/2007#video.responses" type="application/atom+xml" href="http://gdata.youtube.com/feeds/api/videos/NJPrllhYZrg/responses?v=2" />
    <link rel="http://gdata.youtube.com/schemas/2007#video.ratings" type="application/atom+xml" href="http://gdata.youtube.com/feeds/api/videos/NJPrllhYZrg/ratings?v=2" />
    <link rel="http://gdata.youtube.com/schemas/2007#video.complaints" type="application/atom+xml" href="http://gdata.youtube.com/feeds/api/videos/NJPrllhYZrg/complaints?v=2" />
    <link rel="http://gdata.youtube.com/schemas/2007#video.related" type="application/atom+xml" href="http://gdata.youtube.com/feeds/api/videos/NJPrllhYZrg/related?v=2" />
    <link rel="http://gdata.youtube.com/schemas/2007#mobile" type="text/html" href="http://m.youtube.com/details?v=NJPrllhYZrg" />
    <link rel="self" type="application/atom+xml" href="http://gdata.youtube.com/feeds/api/users/smartdraw/uploads/NJPrllhYZrg?v=2" />
    <author>
      <name>smartdraw</name>
      <uri>http://gdata.youtube.com/feeds/api/users/smartdraw</uri>
    </author>
    <yt:accessControl action="comment" permission="allowed" />
    <yt:accessControl action="commentVote" permission="allowed" />
    <yt:accessControl action="videoRespond" permission="moderated" />
    <yt:accessControl action="rate" permission="allowed" />
    <yt:accessControl action="embed" permission="allowed" />
    <yt:accessControl action="syndicate" permission="allowed" />
    <gd:comments>
      <gd:feedLink href="http://gdata.youtube.com/feeds/api/videos/NJPrllhYZrg/comments?v=2" countHint="0" />
    </gd:comments>
    <media:group>
      <media:credit role="uploader" scheme="urn:youtube">smartdraw</media:credit>
      <media:description type="plain">Explains the features of SmartDraw, the software that allows you produce any visual, whether it's a flowchart or a floor plan, in a matter of minutes.
Download a free trial of SmartDraw here: http://www.smartdraw.com/downloads/?id=343742</media:description>
      <media:keywords>SmartDraw, flowcharts, visuals, communicate, visually, communication, powerpoint, presentations, mind, maps, software, Business, graphics, strategic, planning, solutions, tools</media:keywords>
      <media:player url="http://www.youtube.com/watch?v=NJPrllhYZrg&amp;feature=youtube_gdata" />
      <media:thumbnail url="http://i.ytimg.com/vi/NJPrllhYZrg/default.jpg" height="90" width="120" time="00:03:21" />
      <media:thumbnail url="http://i.ytimg.com/vi/NJPrllhYZrg/2.jpg" height="90" width="120" time="00:03:21" />
      <media:thumbnail url="http://i.ytimg.com/vi/NJPrllhYZrg/1.jpg" height="90" width="120" time="00:01:40.500" />
      <media:thumbnail url="http://i.ytimg.com/vi/NJPrllhYZrg/3.jpg" height="90" width="120" time="00:05:01.500" />
      <media:thumbnail url="http://i.ytimg.com/vi/NJPrllhYZrg/hqdefault.jpg" height="360" width="480" />
      <media:title type="plain">SmartDraw 2010 Guided Tour</media:title>
      <yt:duration seconds="402" />
      <yt:uploaded>2010-02-10T23:27:38.000Z</yt:uploaded>
      <yt:videoid>NJPrllhYZrg</yt:videoid>
    </media:group>
    <gd:rating average="4.4444447" max="5" min="1" numRaters="9" rel="http://schemas.google.com/g/2005#overall" />
    <yt:statistics favoriteCount="9" viewCount="16206" />
    <yt:rating numDislikes="1" numLikes="8" />
  </entry>
</feed>

Now, here are use cases for how I might want to use this format:

  1. The GData API will only serve a maximum of 50 entries at any given time - one way to paginate through all of the entries in one go is to parse the link rel="next" field and query that URL until the field no longer exists. Is there a way you can use a POCO class in Hammock or RestSharp to automatically grab this field between queries against the API?
  2. Imagine you create a POCO class which contains the YouTube video ID, the author's username, the number of comments on the video, the keywords for the video, the number of views, and the number of the number of favorites. How would you structure this class such that RestSharp or HammockREST can automatically parse it from an ATOM response format like the one above? Bear in mind that these fields come from four of the five different XML namespaces (atom, Media RSS [media], GData [gd], and YouTube [yt]) used in this response format.

If you enjoyed this post, make sure you subscribe to my RSS feed!



Two Ways to Randomize IList<T> Objects

June 7, 2010 12:27 by Aaronontheweb in .NET // Tags: , , , // Comments (0)

I recently developed a self-sorted IList implementation for a project and I needed some automated way to unit test it - so naturally, the best way to automatically test a sorting function is to force it to sort the results of an unsorting function ;). Just for reference, IList is the interface implemented by every sort of List<T> variant in the .NET Collections library, with the notable exception of SortedList which is really a misnomer given that it actually implements IDictionary instead of IList.

I was excited at the prospect of developing my own unsorting method, but sadly a quick Bing query revealed a couple of solutions developed by programmers far more diligent than I, and I decided to roll both of their solutions into a pair of generic extension methods which I have tested and built into my solution. Let's take a look at them:

Solution 1 - Randomizing an IList<T> Object Using LINQ Projection

By far the most simple, yet elegant method for randomizing the contents of an IList object I found was this one developed by Sven Groot, which uses LINQ's OrderBy method to randomize the contents of an IEnumerable object:

public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
    Random rnd = new Random();
    return source.OrderBy<T, int>((item) => rnd.Next());
}

In terms of brevity, it doesn't get much better than this. Also, given that I need to ultimately create an IList<T> object out of this randomized collection remember that List<T> has a constructor which accepts an IEnumerable<T> collection as an argument.

Solution 2 - Randomizing an IList<T> Object Using an Index Swap Routine

The more traditional, pre-LINQ solution would be to randomize the contents of an IList<T> object using a swap routine given that all IList objects have indexers, which is exactly what Jon Skeet did using this solution:

Random rng = new Random();
for (int i = list.Count - 1; i > 0; i--)
{
    int swapIndex = rng.Next(i + 1);
    if (swapIndex != i)
    {
        //Changed this from "object" to "T" in order to support generics.
        object tmp = list[swapIndex];
        list[swapIndex] = list[i];
        list[i] = tmp;
    }
}

If you have time, make sure you read Jon's explanation of how the algorithm works (it's pretty cool imho.) I went ahead and wrapped this code into an extension method just like Sven's LINQ solution, and here's what that looks like:

public static IEnumerable<T> RandomizeWithSwaps<T>(this IList<T> list)
{
    Random rng = new Random();
    for (int i = list.Count - 1; i > 0; i--)
    {
        int swapIndex = rng.Next(i + 1);
        if (swapIndex != i)
        {
            //Changed this from "object" to "T" in order to support generics.
            T tmp = list[swapIndex];
            list[swapIndex] = list[i];
            list[i] = tmp;
        }
    }

    return list;
}

One thing worth noting is that this extension method will only work with IList types - IEnumerable doesn't have any support for indexers, thus this extension method is somewhat wonky. Given that, I went with using the LINQ solution in my unit testing code.

If you enjoyed this post, make sure you subscribe to my RSS feed!



Frustration Central: Parsing the DateTime Values from the SlideShare REST API

June 2, 2010 09:49 by Aaronontheweb in .NET, SlideShare // Tags: , , , , // Comments (8)

I feel a little bad about posting this given that Jon Boutelle, the CTO of SlideShare, already admitted that this portion of the SlideShare 2.0 REST API sucks and that they're going to fix it eventually, but given that I'm in the middle of rewriting my original SlideShare Presentation XML deserializer I wanted to post my current solution to this problem and solicit the opinion of the .NET community in order to find a more elegant solution, if one exists.

Here's the problem - if you're trying to query any number of presentation objects from SlideShare, you're inevitably going to make a call to get_slideshow or to some method which depends on it. That's an unavoidable fact of life when you're dealing with the SlideShare API. For the most part, the SlideShare API's response format is intuitive and intelligible - here's an example response using actual data:

<?xml version="1.0" encoding="utf-8"?>
<Slideshow>
  <ID>3997766</ID>
  <Title>Startup Metrics 4 Pirates (Montreal, May 2010)</Title>
  <Description>slides from my talk at Startup Camp Montreal 6 (May, 2010)</Description>
  <Status>2</Status>
  <Username>dmc500hats</Username>
  <URL>[removed for fomatting reasons]</URL>
  <ThumbnailURL>[removed for fomatting reasons]</ThumbnailURL>
  <ThumbnailSmallURL>[removed for fomatting reasons]</ThumbnailSmallURL>
  <Embed>
    [removed for fomatting reasons]
  </Embed>
  <Created>Thu May 06 14:10:46 -0500 2010</Created>
  <Language>en</Language>
  <Format>ppt</Format>
  <Download>1</Download>
  <Tags>
    <Tag Count="1" Owner="1">startup</Tag>
    <Tag Count="1" Owner="1">scmtl</Tag>
    <Tag Count="1" Owner="1">leanstartup</Tag>
    <Tag Count="1" Owner="1">acquisition</Tag>
    <Tag Count="1" Owner="1">activation</Tag>
    <Tag Count="1" Owner="1">poutin</Tag>
    <Tag Count="1" Owner="1">pirate</Tag>
    <Tag Count="1" Owner="1">metrics</Tag>
    <Tag Count="1" Owner="1">referral</Tag>
    <Tag Count="1" Owner="1">aarrr</Tag>
    <Tag Count="1" Owner="1">retention</Tag>
    <Tag Count="1" Owner="1">revenue</Tag>
  </Tags>
  <NumDownloads>18</NumDownloads>
  <NumViews>1002</NumViews>
  <NumComments>0</NumComments>
  <NumFavorites>3</NumFavorites>
  <NumSlides>67</NumSlides>
   <RelatedSlideshows>
    <RelatedSlideshowID rank="6">89026</RelatedSlideshowID>
    <RelatedSlideshowID rank="4">602558</RelatedSlideshowID>
    <RelatedSlideshowID rank="3">629696</RelatedSlideshowID>
    <RelatedSlideshowID rank="5">629833</RelatedSlideshowID>
    <RelatedSlideshowID rank="9">1064559</RelatedSlideshowID>
    <RelatedSlideshowID rank="10">1566287</RelatedSlideshowID>
    <RelatedSlideshowID rank="1">2992302</RelatedSlideshowID>
    <RelatedSlideshowID rank="2">3017886</RelatedSlideshowID>
    <RelatedSlideshowID rank="8">3387416</RelatedSlideshowID>
    <RelatedSlideshowID rank="7">3951684</RelatedSlideshowID>
   </RelatedSlideshows>
  <PrivacyLevel>0</PrivacyLevel>
  <SecretURL>0</SecretURL>
  <AllowEmbed>0</AllowEmbed>
  <ShareWithContacts>0</ShareWithContacts>
</Slideshow>

Intuitive, no? However, let's take a closer look at the <Created> field, which contains the date that this SlideShare presentation was originally uploaded:

<Created>Thu May 06 14:10:46 -0500 2010</Created>

What, pray tell, is this? It appears that SlideShare REST 2.0's date format consists of the following elements in left to right order:

<Created>Day of week name, Month name, Day of month, Hours:Minutes:Seconds, UTC offset, Year</Created>

Unfortunately, this format is not one of the many supported Standard Date and Time Formats in .NET nor is it a UNIX time format, so DateTime.Parse and the other built-in DateTime parsing variants in .NET will not be able to parse this DateTime data from the SlideShare API. Thus, I created my own admittedly ugly solution to this problem, which I will reveal below, every ugly piece by ugly piece.

Despite how ugly this solution is, it's been tested pretty thoroughly and works (as far as I know.) The only thing it does not do, currently, is use the UTC offset in any way, shape or form, which is a modification I am currently working on in my new version of the SlideShare presentation deserializer which uses these functions.

The first issue is that there's no numeric value for the month - you have to map a three-letter code to its correspodning month, i.e. "Mar" = 3, "May" = 5, and so forth. Here's the function I made for that very purpose:

public static int GetMonth(string slideShareDatetime)
{
    try
    {   //Skip past the three-letter code for the day of the week and the space in between.
        string MonthStr = slideShareDatetime.Substring(4, 3).ToLower();
        int returnValue = 1;
        switch (MonthStr)
        {
            case "jan":
                returnValue = 1;
                break;
            case "feb":
                returnValue = 2;
                break;
            case "mar":
                returnValue = 3;
                break;
            case "apr":
                returnValue = 4;
                break;
            case "may":
                returnValue = 5;
                break;
            case "jun":
                returnValue = 6;
                break;
            case "jul":
                returnValue = 7;
                break;
            case "aug":
                returnValue = 8;
                break;
            case "sep":
                returnValue = 9;
                break;
            case "oct":
                returnValue = 10;
                break;
            case "nov":
                returnValue = 11;
                break;
            case "dec":
                returnValue = 12;
                break;
            default:
                throw new InvalidOperationException("Unable to recognize month" + MonthStr + " in SlideShareDate.");
        }
        return returnValue;
    }
    catch (Exception ex)
    {
        logger.ErrorException("Unable to parse month from SlideShare presentation.", ex);
        throw;
    }
}

The next thing we have to do is extract the numeric day of the week, which I do using the function below:

public static int GetDay(string slideShareDatetime)
{
    string strRegexPattern = @"((\d|\d{2})\b){1}";
    Regex r = new Regex(strRegexPattern);
    string matchingDay = r.Match(slideShareDatetime).ToString();
    return Convert.ToInt32(matchingDay);
}

I tested several regular expressions numerous times before I settled on this one, which I liked for its simplicity - all it does is look for the first instance of a one or two numeric character word in a given string.

Next, we have to extract the year from the string, which I do using the function below:

public static int GetYear(string slideShareDatetime)
{
    string YearStr = slideShareDatetime.Substring(slideShareDatetime.Length - 4, 4);
    return Convert.ToInt32(YearStr);
}

All this function does is extract the last four characters from the string (the date, in this case) and converts it into an integer. I suppose this function will break sometime around the year 10,000, or whenever SlideShare adds some whitespace to the end of their XML response for the Created field, whichever comes first.

Lastly we have to extract the hour:minutes:seconds values into some sort of intelligible format, so thus I present you with yet another cringe-worthy function:

public static Dictionary GetTime(string slideShareDatetime)
{
    try
    {
        Dictionary returnValue = null;

        string strRegexPattern = @"((\d{2}:\d{2}:\d{2})\b)";
        Regex r = new Regex(strRegexPattern);

        string matchingDay = r.Match(slideShareDatetime).ToString();
        if (matchingDay == String.Empty)
            throw new InvalidOperationException("Unable to parse time of day from SlideShareDate:" + slideShareDatetime);

        string[] TimeParts = matchingDay.Split(':');
        if (TimeParts.Length < 3)
            throw new InvalidOperationException("Invalid date/time parsed from SlideShareDate:" + slideShareDatetime);

        returnValue = new Dictionary();
        returnValue.Add("hours", Convert.ToInt32(TimeParts[0]));
        returnValue.Add("minutes", Convert.ToInt32(TimeParts[1]));
        returnValue.Add("seconds", Convert.ToInt32(TimeParts[2]));

        return returnValue;
    }
    catch (Exception ex)
    {
        logger.ErrorException("Failed to parse time from presentation.", ex);
        throw;
    }
}

And then we put it all together in one, big, happy function:

public static DateTime ParseSlideShareDateTime(string slideShareDatetime)
{
    int Month = GetMonth(slideShareDatetime);
    int Year = GetYear(slideShareDatetime);
    int Day = GetDay(slideShareDatetime);
    Dictionary Time = GetTime(slideShareDatetime);
    DateTime properDate = new DateTime(Year, Month, Day, Time["hours"], Time["minutes"], Time["seconds"]);
    return properDate;
}

Yeah. So - can anybody think of a much more, ahem, "robust" way of parsing the Created field from the SlideShare API? I would love to hear it! :)

If you enjoyed this post, make sure you subscribe to my RSS feed!



How to Use the Microsoft Enterprise Library Validation Application Block, Part 1

All user input is evil, and you know it. Since the inception of .NET, ASP.NET developers have had access to the ASP.NET Validators Control Library, which made the previously tedious process of validating form input simple and in many instances, trivial. Microsoft's Enterprise Library makes this familiar ASP.NET functionality available at the object level in your applications via the Validation Application Block, which I've been using throughout some of my new projects.

It can take a while to get familiar with the Validation Application Block, so my goal here is to show you how to quickly get started with it using attribute-based validation, which in my opinion is the simplest way to implement validation. By the time you're doing reading this you'll know how to use built-in validators such as the RangeValidator, RegexValidator, DateTimeRangeValidator, and so forth.

Introduction to the Validation Application Block Built-in Validators

What can the Validation Application Block do, exactly? Well, here's a list of the validators that are built-in to the Validation Application Block:

  • The NotNullValidator which requires an instantiated data member.
  • The RangeValidator and the DateTimeRangeValidator, which check to see that a data member's value falls within a specified set of boundary conditions.
  • A slew of validators for testing strings:
    • The StringLengthValidator for testing that a string's length falls within a specified set of boundary conditions.
    • The RegexValidator and the ContainsCharactersValidator, both of which test that a string contains a specified target sub-string or expression.
    • The TypeConversionValidator, which tests to see if a string can be converted to some other type.
  • Validators for testing the comparative values of fields, such as the PropertyComparisonValidator and the RelativeDateTimeValidator.
  • The DomainValidator and the EnumValidator, which checks to see that a value falls within a specified set of acceptable values.
  • The ObjectValidator and the ObjectCollectionValidator, which are used to invoke validation methods on data members which are also validation-enabled objects.

In addition to these built-in validators the Validation Application Block also includes methods for creating user-defined validators and for composing validators into ValidatorCompositions which can process more complex validation rules.

How to Add the Validation Application Block to a Visual Studio Project

Before you worry about how to start adding validators to all of your business objects, you need to include the necessary assemblies into your Visual Studio / Visual [X] Express projects. After you've downloaded and installed the Enterprise Library 5.0 or *Enterprise Library 4.1 binaries onto your system, open your existing Visual Studio Project or create a new one. Open the Solution Explorer, expand the References folder, right click on the References folder and click on Add Reference

Select the following two assemblies (typically they'll be installed to your system's GAC so you won't need to go hunting for them) - Enterprise Library Shared Library and Enterprise Library Validation Application Block. The Validation Application Block depends on Shared Library (as do many other components in the Enterprise Library), and you'll run into some run-time errors without it included in your project's References folder.

If you're designing a WinForms or an ASP.NET application, you may want to include the special Validation Application Block WinForms Integration or Validation Application Block ASP.NET Integration assemblies, but if you're designing validation rules for individual business object classes and not presentation-layer events, you may not need them.

Example: Using Built-in Validators for Validating an Address

I'm going to end this post with a simple example which will show you how to get started using the Validation Application Block's attribute-based validators, and I'll expand on this in subsequent posts. We're going to work with a simple class which contains an address based in the United States:

public class USAddress
{
    public string RecipientName { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string ZipCode { get; set; }
}

We know that addresses based in the United States need to comply with the following business rules:

  1. All of these fields must be filled in;
  2. A street must contain a street name and a street number;
  3. A state code must be two characters in length; and
  4. A zip code contains exactly five or nine digits.

 So, let's examine how we can use some validators to ensure that this business object enforces these business rules. The first step is to ensure that our source code references the correct namespace:

using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;

 Next, let's go ahead and enforce business rule #1 - all address fields must be filled in. We can accomplish by decorating each field with NotNullValidator attribute:

using System;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;

namespace Business_Entities
{
    public class USAddress
    {
        [NotNullValidator()]
        public string RecipientName { get; set; }

        [NotNullValidator()]
        public string Street { get; set; }

        [NotNullValidator()]
        public string City { get; set; }

        [NotNullValidator()]
        public string State { get; set; }

        [NotNullValidator()]
        public string ZipCode { get; set; }
    }
}

Let's see if this works. Here's some simple code we can use to test the validators by calling the Validator.Validate() method against an instance of this class.

using System;
using System.Text;
using Business_Entities;
using Microsoft.Practices.EnterpriseLibrary.Validation;

namespace Basic_Validation__Console_
{
    class Program
    {
        static void Main(string[] args)
        {
            //Create a blank test address
            USAddress testaddress = new USAddress();

            //Create a new validator using the ValidationFactory method
            Validator validator = ValidationFactory.CreateValidator<USAddress>();
            ValidationResults results = new ValidationResults();
            validator.Validate(testaddress, results);

            Console.Write("Is the test address valid? {0}", results.IsValid);
            //Stop the console screen from disappearing immediately.
            Console.ReadKey(); 
        }
    }
}

And what happens when we run this test code? 

Smells like success to me. Let's re-run this test using a valid address.

//A real(ish) test address
USAddress testaddress = new USAddress();
testaddress.RecipientName = "Bob Jones";
testaddress.City = "Beverly Hills";
testaddress.State = "CA";
testaddress.Street = "1205 Rodeo Drive";
testaddress.ZipCode = "90210";

And the result is: 

Looks like the NotNullValidator is working as intended. But wait a minute, how did this ValidationFactory.CreateValidator() method know how to create a Validator object that properly validates any instance of a USAddress object?

Because we decorated the USAddress class with one or more Validator attributes, the underlying Validation Application Block library automatically implements and registers a Validator class designed to support the USAddress class. Internally, the Validation Application Block uses an instance of Unity to resolve a custom validator for the USAddress class on-the-fly.

Let's enforce some of the other business rules, starting with the state code.

[NotNullValidator()]
[StringLengthValidator(2, RangeBoundaryType.Inclusive, 2, RangeBoundaryType.Inclusive)]
public string State { get; set; }

As you can see I've added a StringLengthValidator in addition to the NotNullValidator that was already in place (yes, you can add more than one Validator attribute to a field.) I've specified the following parameters for this validator:

  1. lowerBound = 2
  2. lowerBound RangeBoundaryType = Inclusive (meaning that this value is included in the range of acceptable string length values)
  3. upperBound = 2
  4. upperBound RangeBoundaryType = Inclusive (this value is also included in the range of acceptable string length values)

There are three possible range boundary types:

  1. Inclusive, which is the equivalent of [] in traditional mathematical syntax;
  2. Exclusive, which is the equivalent of () in traditional mathematical syntax; and
  3. Ignore, which is the equivalent of (infinity, or infinity) in traditional mathematical syntax.

Now the next change we need to make is to check for a valid zip code, which can be either 9 or 5 digits. I'm going to do this using a RegexValidator - another way to do this is to create an ValidatorComposition (an "Or" type as opposed to an "And" type) with two different string length validators. Here's what our RegexValidator looks like:

[NotNullValidator()]
[RegexValidator(@"^(\d{5}-\d{4}|\d{5}|\d{9})$|^([a-zA-Z]\d[a-zA-Z] \d[a-zA-Z]\d)$")]
public string ZipCode { get; set; }

Hat tip to Microsoft's Patterns & Practices group for providing me with the regular expression for validating US zip codes.

Now, our source code for testing this validation has not changed one bit - all of the changes to the validator for the USAddress class occur behind the scenes. Once I recompile my class library and re-run the test I should get a "validation successful" message back from the console: 

Well hell yeah, that'll do it! If I wanted to validate the last business rule (the street address must contain both a street name and number,) I could write another RegexValidator for that and hook it up in much the same way as my RegexValidator for zip codes. And that's all it really takes to get started with the Microsoft Enterprise Library Validation Application Block. In my next article on the subject I'll include some examples on how / when you should use ObjectValidators and why they're so useful.

*Although the latest version of the Validation Application Block is version 5.0, my example code uses Enterprise Library version 4.1, namely because my individual development environment has some issues with the Validation Application Block 5.0 and the GAC. Given that there are no major syntactical differences between the two versions, I'm going to do what any economical programmer would do and use what works instead of dicking around with configuration files, assembly directories, and things that are frankly tangential to developing applications.

If you enjoyed this post, make sure you subscribe to my RSS feed!



How to Test Regular Expressions in .NET without Giving Yourself Migraines

May 17, 2010 05:56 by Aaronontheweb in .NET // Tags: , // Comments (1)

I write a lot of parse-heavy applications, so naturally I spend a fair amount of my development time writing and testing regular expressions. Regular expressions are one of those programming constructs where you always have a clear idea of what you need to do but you work with them just infrequently enough that you can never actually remember the exact syntax. And if you're like me, this means a multi-hour regex 101 refresher until you get it right.

Rather than testing my regular expressions in the middle of a unit test or in some temporary debug code, I've started using .NET Framework Regular Expressions Demo (scroll down to the bottom) provided by Regular-Expressions.info. It's a simple Windows Forms application that allows you to run quick regex tests using the System.Text.RegularExpressions engine, which is nice because it's actual regular expressions engine you'll be using in your .NET production code.

Let me show you how it works using a couple of examples. Suppose we've written the following regular expressions (in C#) for testing the validity of a user's email address:

string EmailRegex1 = @"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b";
string EmailRegex2 = @"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$";
string EmailRegex3 = @"^[\w-]+@([\w-]+\.)+[\w-]+$";

We need to verify that these regular expressions work in all of our use cases:

  1. firstname@email.com
  2. fistname.lastname@email.com
  3. firstname@email.subdomain.com
  4. first-name@email.com
  5. Etc...

Let's see how we can do accomplish this using the .NET Regex Demo: 

Plugin your regular expression and your test case into their appropriate fields and hit the match test button.

 

Whoops! Our test case failed? Perhaps it's because the regex doesn't account for case - I'll re-run the test with the System.Text.RegularExpressions' "Case insensitive" option set to true this time.

 

That did the trick - so if I wanted to use this particular regex in my production code, I'd just need to remember to set the Regex engine's IgnoreCase option to true:

string EmailRegex1 = @"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b";

Regex r = new Regex(EmailRegex1, RegexOptions.IgnoreCase);
r.Match("firstname@email.com");

This is way better than testing regular expressions through NUnit tests and debug code.

Download (.zip) http://www.regular-expressions.info/download/csharpregexdemo.zip 

If you enjoyed this post, make sure you subscribe to my RSS feed!



Popular .NET Libraries

May 14, 2010 15:31 by Aaronontheweb in .NET // Tags: , // Comments (0)

I added a page a list of the .NET client libraries I use in my production and development code. Some of these, like NUnit and NLog, are pretty well-known to grizzled .NET developers, but others like the HTML Agility Pack and Hammock are not nearly as well-known as they should be given how powerful and extensive they are.

Check out my list of .NET client libraries and let me know if you have any suggestions.

If you enjoyed this post, make sure you subscribe to my RSS feed!



Search

About

My name is Aaron, I'm an entrepreneur and a .NET developer who develops web, cloud, and mobile applications.

I left Microsoft recently to start my own company, MarkedUp - we provide analytics for desktop developers, focusing initially on Windows 8 developers.

You can find me on Twitter or on Github!

Recent Comments

Comment RSS

Sign in