Red, Green, Chris

May 20th, 2008

Yes I know, it is supposed to be Red, Green, Refactor, but as a elegant coder I have access to Chris who is a carbon based refactoring engine. 

I had some code that I was unhappy with.  Basically one of my constructors loads a embedded XML document and uses LINQ to XML to set some standard properties on the class.   Since I know Chris is a expert on all things LINQ I sent him the constructor I had written and asked if he could see how to make it better.  Here is the code for the original constructor:

 1:  Public Dice()
 2:  {
 3:              // Get the XElments for the game basics
 4:              XElement basics = EmbeddedResources.GetEmbeddedXElement(
                     "OpenGamingLicense.Framework", "Resources.Basics.xml");
 5:   
 6:              // Query for the nodes for this class
 7:              var fos = from dice in basics.Elements("Dice")
 8:                       select new
 9:                       {
10:                           name = (string)dice.Element("Name"),
11:                           description = (string)dice.Element("Description"),
12:                       };
13:   
14:              // foreach node (should oly be 1) set the name, description 
                       and license info for this class
15:              foreach (var fo in fos)
16:              {
17:                  Name = fo.name;            
18:                  Description = fo.description;
19:                  Legal = (string) basics.Element("Legal");
20:              }
21:  }

So I was pretty unhappy with the way the code at line 7 worked.  Basically I know there will only be one element in the XML document that matches the Elements("Dice") Criteria.  But all the examples I had showed using a foreach construct to return the data for the node so I fired of a email to Chris to see if he had any suggestions.  In the span of 25 minutes I got back 5 emails.  His first cut looked like this:

   1:  // Query for the nodes for this class
   2:  var fos = from dice in basics.Elements("Dice")
   3:  select new Dice
   4:          {
   5:             name = (string)dice.Element("Name"),
   6              description = (string)dice.Element("Description"),
   7:   legal = (string) basics.Element("Legal");
   9:          };

Which was cool and got rid of the foreach loop but still had some overhead that he was not happy with.  So this was his second attempt (mind you these are flying at me in a 4 minute time span)

     1: var fos = (from dice in basics.Elements("Dice")

   2: select new {name = (string)dice.Element("Name"),
                  description = (string)dice.Element("Description"),}).First();
   3:   
   4: Name = fos.name;
   5: Description = fos.description;
   6: Legal = (string)basics.Element("Legal");

 

Cleaned up a bit and closer to what I wanted.  So here was the third version

 

   1: var mydice = (from dice in basics.Elements("Dice") select dice).First();
   2: Name = (string)mydice.Element("Name");
   3: Description = (string)mydice.Element("Description");
   4:   
   5: Legal = (string)basics.Element("Legal");

 

So from 21 lines to 5.  This one is also much cleaner and takes advantage of the fact the first node is the only node.  So now parsing the name and description is much simpler.   I was pretty happy with this version until about 8 minutes later when I get this in an email

 

   1: public Dice() : 
        this(EmbeddedResources.GetEmbeddedXElement("OpenGamingLicense.Framework", 
              "Resources.Basics.xml"))
   2: {
   3: }
   4:          
   5: public Dice(XElement basics)
   6: {
   7:      var mydice = (from dice in basics.Elements("Dice") select dice).First();
   8:      Name = (string)mydice.Element("Name");
   9:      Description = (string)mydice.Element("Description");
  10:      Legal = (string)basics.Element("Legal");
  11: }

 

Chris had pointed out the original versions of the constructor did not allow for testing.  Now I can still easily access the embedded XML document for the dll using the empty constructor on line 1 or I can test the constructor by passing in an XElement.   All really good ideas and taken care of in a few minutes.  In addition he had reduced 21 lines of code to 11 and added the ability to test the constructor. As Dave pointed out,  Chris is a machine. :-).  

Thanks Dude!

Darrel Carver Uncategorized , , , , ,

  1. Steve
    May 20th, 2008 at 03:18 | #1

    I know writing LINQ queries is cool and all but:

    var mydice = (from dice in basics.Elements(”Dice”) select dice).First();

    can just be written as:

    var mydice = basics.Elements(”Dice”).First();

  2. May 20th, 2008 at 03:51 | #2

    @Steve

    Absolutely true. However what I wanted to know was how to do it in LINQ to XML. It was more of a learning exercise then anything else. I have more complex examples for LINQ to XML coming up. This was just a start.

  3. May 20th, 2008 at 06:52 | #3

    SO, Darrel, did you write the tests?

  4. Huh?
    May 21st, 2008 at 02:01 | #4

    “However what I wanted to know was how to do it in LINQ to XML”

    var mydice = basics.Elements(”Dice”).First();

    THAT IS LINQ TO XML!

    and if there really shoud be only one:
    var mydice = basics.Elements(”Dice”).Single();

  5. Steve
    May 21st, 2008 at 03:40 | #5

    Uhm, sure it is, it’s calling the extension method that is provided by Linq to Xml, being able to use select from is just a compiler feature, the only different is that it will also generate a stupid .Select(dice => dice) extra…

  6. Steve
    May 21st, 2008 at 03:43 | #6

    I’m sorry, the First (or Single) method has nothing to do with Linq to Xml.
    It’s actually only Elements() that is a linq to xml extension method.

    But the fact remains, they both are using Linq to Xml whether you write it as select from or without it :)

  7. May 22nd, 2008 at 01:38 | #7

    @Steve

    Good point. So basically you see line 7 as

    var mydice = basics.Elements(”Dice”).Single();

    instead of

    var mydice = (from dice in basics.Elements(”Dice”) select dice).First();

    Interesting since the new version of line 7 drops the select. This works well when we are not using more advanced features of LINQ to SQL like joins.

Comments are closed.