August 2006 - Posts

VB9 Prepares for World Domination

With people like Erik Meijer and Brian Beckman leading the charge, Visual Basic is on a course for World Dominiation .  Checkout the channel 9 video with Brian to learn more about how they're aiming to make VB the language of choice among the developers of the world.  As I stated before there are a lot of very nice features making their way into VB that could begin to grab my attention away from C#.  Then again maybe not

Tracking tasks in a browser?

This past week we started experimenting with a "long distance collaboration tool" called CardMeeting.  Our team has a big ocean in between them which sometimes makes it difficult for everyone to stay on the same page.  As I mentioned in How to prevent an agile face plant post, we use index cards for all of our planning.  We have a big visible whiteboard that "holds" all of our cards.  As we start a task we "brown it", and when we finish it we "green it".  At times the card can go red as well which means its blocked due to something development can't control (such as needing clarification from business).

Our whiteboard has a lot of valuable information.  At any given time anyone can walk over to the whiteboard and see where we're at.  Unless of course they're across the ocean.  To help keep our colleagues across the pond in the loop this past week we used CardMeeting to hold a duplicate "copy" of all of our cards.  This allowed everyone on the project to have the latest and greatest information about where we were.  For the development team it was a bit of a pain maintaining two copies of all the cards, but, being the great team that we are we managed. 

In addition to using it to track the current iteration we've also started a couple of other card meetings which are being used to plan out future iterations.  I'm not exactly sure how we'll use it going forward, and if it will be useful or not, but at the very least it's an interesting experiment.

tags: , ,

Generating an RSS Feed from the Event Log using Linq to XML

Jim Wooley put together a nice "cool code" sample for the Jacksonville Code Camp.  Obviously anything that uses Linq gets my vote.  He took third place for his Linq to XML code that generates an RSS feed from the Event Log using VB9's XML Literals.

Download

tags: , ,

Called on the carpet for not solving ALL Scoble's problems

M. David Peterson calls me on the carpet for not really solving ALL of Scoble’s problems and for not using a fancy algorithm to boot.  I was waiting for someone to expose me and it appears it’s happened.    Anywho, he has a nice post on how he solved Scoble’s problem using XSLT.  He ends up with 29 lines of XML (data + transform) compared to my 48 lines of Linq code.  Of course I like my version much more since it uses Linq but his post does point out an important point, while Linq and Linq to XML do an amazing job of providing a consistent programming API for accessing all sorts of data there may be times when XSLT (or some other technology) are more – maybe not more but just as – appropriate.  I for one really like what Linq provides.  The fact that I can write what is essentially transform code in my preferred programming language (C#) using my preferred data query api (Linq) and do so rather quickly rocks.

tags: , , ,

Solving all Scoble's problems with Linq to XML

Although Scoble already received a lot of answers on how he can get get what he wants out of his Excel file containing all the URL's from the http://weblogs.com/changes.xml file I couldn't help but fire up VS and solve his problem using Linq to XML.  I'm sure Scoble will send me a big fat check for solving his problem with one of the cooler technologies that will coming out of his former employer.  Of course I didn't give him just the URL's that he asked for either so maybe I shouldn't hold my breath.  On the other hand maybe he'll give me a bonus for giving him a nice list that has the number of sites for each site he's interested in grouped with a count? 

Output:

  • Blogspot has 8928 sites in the changes.xml file
    • list of sites
  • Spaces has 900 sites in the changes.xml file
    • list of sites
  • Wordpress has 384 sites in the changes.xml file
    • list of sites
  • TypePad has 118 sites in the changes.xml file
    • list of sites

Code:

    1 using System;

    2 using System.Collections.Generic;

    3 using System.Text;

    4 using System.Query;

    5 using System.Xml.XLinq;

    6 using System.Data.DLinq;

    7 

    8 namespace ScobleWeblogsDotComCleaner {

    9   class Program {

   10     static void Main(string[] args) {

   11       XElement weblogs = XElement.Load("http://rpc.weblogs.com/changes.xml");

   12 

   13       var matches =

   14         from weblog in weblogs.Elements("weblog")

   15         where IsMatch((string) weblog.Attribute("url"))

   16         group weblog by GetSite((string) weblog.Attribute("url")) into sites

   17         orderby sites.Count() descending

   18         select new {

   19             HostingSite=sites.Key,

   20             Count=sites.Count(),

   21             Sites=sites

   22           };

   23 

   24       foreach(var match in matches) {

   25         Console.WriteLine(match.HostingSite + " has " + match.Count + " sites in the changes.xml file");

   26         foreach(var x in match.Sites) {

   27           Console.WriteLine("  - " + (string) x.Attribute("url"));

   28         }

   29       }

   30     }

   31 

   32     private static string GetSite(string  url) {

   33       if(url.Contains("spaces.live.com")) return "Spaces";

   34       else if(url.Contains("typepad.com")) return "TypePad";

   35       else if(url.Contains("blogspot.com")) return "Blogspot";

   36       else if(url.Contains("wordpress.com")) return "Wordpress";

   37       return String.Empty;

   38     }

   39 

   40     private static bool IsMatch(string url) {

   41         return

   42           url.Contains("spaces.live.com") ||

   43           url.Contains("typepad.com") ||

   44           url.Contains("blogspot.com") ||

   45           url.Contains("wordpress.com");

   46     }

   47   }

   48 }

 

tags: , , ,

LINQ to Entities vs. LINQ to SQL

Kevin Hoffman has a nice post outlining the differences between LINQ to Entities and LINQ to SQLAs I've stated before I think LINQ to SQL should go by the wayside for LINQ to Entities.  If you're going to create a conceptual model that directly maps to your relational model you can still use LINQ to Entities.  Why lock yourself into a direct mapping when you can instead have the flexibility of a conceptual model which can change independently of your relational model?  LINQ to Entities is where it's at.  Let's all accept that and start using it.  Whenever you have the question of whether LINQ to SQL or LINQ to Entities is your best approach go with LINQ to Entities. 

tags: , , , , ,

Creating an RSS Feed using LINQ to XML (XLinq)

To help get more familiar with the new XML Programming API that is hidden away inside of LINQ to XML I recently set on a journey to update the code that creates my RSS feed to use LINQ to XML.  In order to complete my journey I needed to get familiar with how to use functional construction to build XML from a set of in memory objects.  Before diving into the LINQ to XML code let's first take a peak at the old code which was building the XML using an XmlWriter.

    1   StringWriter sw = new StringWriter();

    2   XmlTextWriter xml = new XmlTextWriter(sw);

    3   xml.WriteProcessingInstruction("xml-stylesheet", "href=\"" + GetRootUrl() + "/friendly-rss.xsl\" type=\"text/xsl\" media=\"screen\"");

    4   xml.WriteStartElement("rss");

    5   xml.WriteAttributeString("version", "2.0");

    6   xml.WriteAttributeString("xmlns:dc", "http://purl.org/dc/elements/1.1/");

    7   xml.WriteAttributeString("xmlns:slash", "http://purl.org/rss/1.0/modules/slash/");

    8   xml.WriteAttributeString("xmlns:wfw", "http://wellformedweb.org/CommentAPI/");

    9   xml.WriteStartElement("channel");

   10   xml.WriteElementString("title", channel.DisplayName));

   11   xml.WriteElementString("link", GetRootUrl() + "/" + channel.Url);

   12   xml.WriteElementString("generator", "ActiveType CMS v0.1");

   13   xml.WriteElementString("dc:language", "en-US");

   14   xml.WriteElementString("description", channel.Description);

   15 

   16   foreach(Posting posting in postings) {

   17     xml.WriteStartElement("item");

   18     xml.WriteElementString("dc:creator", posting.ActiveRevision.LastModifiedBy.FullName);

   19     xml.WriteElementString("title", posting.ActiveRevision.DisplayName);

   20     xml.WriteElementString("link", postLink);

   21     xml.WriteElementString("pubDate", posting.ActiveRevision.CreatedDate.ToString("r"));

   22     xml.WriteElementString("guid", postLink);

   23     xml.WriteElementString("comments", postLink + "#comments");

   24     xml.WriteElementString("wfw:commentRss", postLink + "/commentRss.aspx");

   25     xml.WriteElementString("slash:comments", posting.NumberOfComments.ToString());

   26     WriteDescriptionElement(xml, true, posting.ActiveRevision.AllowContentFiltering, posting.ActiveRevision.Content, shareLinks);

   27     xml.WriteEndElement();

   28   }

   29 

   30   xml.WriteEndElement();

   31   xml.WriteEndElement();

   32   xml.Close();

I've re-organized the code for this post but most of the basics have stayed intact.  As you can see we use an XmlWriter (XmlTextWriter to be more specific) to write our root rss element, our main channel, as well as the 25 most recent postings.  The LINQ to XML code looks somewhat similar. 

    1   XNamespace dc = "http://purl.org/dc/elements/1.1/";

    2   XNamespace slash = "http://purl.org/rss/1.0/modules/slash/";

    3   XNamespace wfw = "http://wellformedweb.org/CommentAPI/";

    4 

    5   XDocument rss = new XDocument(

    6     new XProcessingInstruction("xml-stylesheet", "href=\"" + GetRootUrl() + "/friendly-rss.xsl\" type=\"text/xsl\" media=\"screen\""),

    7     new XElement("rss", new XAttribute("version", "2.0"),

    8       new XElement("channel",

    9         new XElement("title", channel.DisplayName)),

   10         new XElement("link", GetRootUrl() + "/" + channel.Url),

   11         new XElement("generator", "ActiveType CMS v0.1"),

   12         new XElement(dc + "language", "en-US"),

   13         new XElement("description", GetChannelDescription(channel)),

   14 

   15         from p in postings.OfType<Posting>()

   16         where p.ActiveRevision.IsSyndicated && p.ActiveRevision.Content.Length > 0

   17         select new XElement("item",

   18                 new XElement(dc + "creator", p.ActiveRevision.LastModifiedBy.FullName),

   19                 new XElement("title", p.ActiveRevision.DisplayName),

   20                 new XElement("link", GetLink(p)),

   21                 new XElement("pubDate", p.ActiveRevision.CreatedDate.ToString("r")),

   22                 new XElement("guid", GetLink(p)),

   23                 new XElement("comments", GetLink(p) + "#comments"),

   24                 new XElement(wfw + "commentRss", GetLink(p) + "/commentRss.aspx"),

   25                 new XElement(slash + "comments", p.NumberOfComments.ToString()),

   26                 GetDescription(p)

   27           )

   28       )

   29     )

   30   );

The main difference is that we use functional construction to build our XML using a single statement (albeit spread across many lines) rather then writing each element/attribute using the XmlWriter.  It should also be noted that the sample code using the XmlWriter had to be simplified a good bit in order to fit within this post and be readable.  The LINQ to XML code hasn't been changed at all (well mostly).  The above code shows a couple things which should be pointed out.

  • To create elements that use namespaces we create an XNamespace and append the XNamespace to the local name for the element.  Notice that the XNamespace has an implicit conversion from a string and can be added to the local name to create an XName. (new XElement(dc + "language", "en-US"))
  • Notice that we can create XElements using either hard coded strings or via method calls.
  • Since our query (line 15-16) returns an IEnumerable<XElement> and the XElement constructor is capable of taking an IEnumerable<XElement> and adding each item as a child element we can directly embed a query in the middle of our functionally constructed document.
  • Even though we're using old school collections in this code (postings is a custom collection that inherits from ArrayList) we can use the collection as the basis of our query by way of the OfType<T>() extension method.  The OfType extension method converts an old school collection into an IEnumerable<T> which makes it possible to use it in the from clause of our query.
  • The resulting XML will not have xml namespace prefixes.  In order to get prefixes output we need to do some other work which I wasn't up for just yet.  Rather then outputting <dc:language>en-US</dc:language> we'll get <language xmlns="http://purl.org/dc/elements/1.1/">en-US</language>.  I'll talk more about namespaces and namespaces prefixes in another post so if your interested in how to get LINQ to XML to serialize your XML using prefixes stay tuned.
  • In addition to adding elements by newing up XElements we can also create a function that returns an XElement and pass that to our constructor and get it added as a child element (line 26).

In future posts I'll dive into some more details about how LINQ to XML improves upon the imperative approach provided by the DOM when creating Xml from scratch, overview how LINQ to XML handles namespaces and namespaces prefixes, as well as introduce some of the key concepts and design goals of LINQ to XML.

tags: , , ,

Exception Handling, Logging, and more with WCF and our Smart Client

Sam has a couple posts outlining some of the great fun we had this past week. 

I’ll leave all the gory details to Sam.  However, I would recommend you take a look at the Exception Handling and Logging Blocks within Enterprise Library (and all the other blocks for that matter) if you haven’t done so recently.  Before this past week it had been a while since I looked at EntLib.  After spending a day working with the Logging and Exception Handling block along with a couple hours of investigation earlier in the week I have to say it is a very nice framework for ensuring your applications are properly instrumented and all your exceptions are handled appropriately (based on policy). 

Parsing WordML using XLinq

Over the last couple of weeks I've been reading a lot of the documentation that Eric White put together for XLinq.  Eric is a "Programmer Writer" for several Xml technologies which makes him responsible for a good bit of the documentation on XLinq. 

Yesterday Eric posted a nice writeup of how he managed to parse WordML using XLinq.  While Eric's writeup shows a lot of the nice features within XLinq I'd like to hightlight a couple.  The obvious first feature to talk about is how nice the querying capabilities are for Xml when using XLinq.  I think we've all come to know and love the querying that Linq provides so lets move onto some other nice features of XLinq as highlighted in Eric's post.

Let's take a look at the query used to retrieve all the annotations in the Word document.  Notice that rather then having to deal with XmlConvert we can simply cast our attribute to a string.  Explicit cast operators have been defined for the classes that you'll make use of most when working with XLinq (XAttribute, XElement).  They allow programmers to work with data within an Xml fragment in a very familiar manner.  Rather then having to work with the XmlConvert class everywhere that we read content out of an attribute or element we can simple cast the value to the proper type and let XLinq handle all the dirty work.

            var commentNodes =
                from annos in wordDoc.Descendants(aml +"annotation")
                where (string)annos.Attribute(w + "type") == "Word.Comment"
                select annos;

Another nicety to point out is how easy it is to deal with the namespaces that are used in WordML.  If we were using existing Xml API's we'd have to new up a namespace manager, setup our namespaces and then make sure everything that "queries" our document uses the mentioned namespace manager.  Not a huge deal, but more painful then necessary.  Within XLinq we can forget about namespace managers since all "Xml Names" within XLinq are fully qualified.  This means that whenever we're querying our document we need to give the full namespace and local name of the element or attribute which we're interested in. 

In Eric's sample code we can see how we deal with namespaces by looking at how their defined and then how they're used when querying the document.  Lets start with a look at how the namespaces are defined:


            XNamespace aml = "http://schemas.microsoft.com/aml/2001/core";
            XNamespace w = "http://schemas.microsoft.com/office/word/2003/wordml";

As we can see an XNamespace consists of an Xml Namespace URI.  XLinq has implicit conversion operators for the XNamespace and XName classes which allows strings to be automagically converted to  the classes, very nice. After the namespaces have been defined its simply a matter of prefixing our local names (XName) with the namespace when issuing our queries.

            var commentNodes =
                from annos in wordDoc.Descendants(aml + "annotation")
                where (string)annos.Attribute(w + "type") == "Word.Comment"
                select annos;

Several features discussed in this post are minor API usability features, but, when they're all taken as a whole they result in a much more usable and productive environment for working with Xml.



Strange Blog Behavior

Apologies to anyone who has been seeing strange results in my RSS feed.  The company that hosts my blog also hosts my old company website.  It appears that something strange has been going on with the DNS which has resulted in my old company website showing up in place of my blog.  This would also include my RSS feed.  Sorry for the mixup, they’ve told me things are setup properly now so should everything should return to normal….hopefully.