Linq to XML Related Posts by ThinqLinq

JSON Literals for VB

One of the stand-out features of VB.Net since VB9 has been the inclusion of XML Literals. Followers of this blog should be well familiar with the concept because I first wrote about XML Literals way back in 2006. With them, we can imbed XML directly into our code as follows:

Dim value = <root>
                <child attrib="foo">bar</child>
            <root>

While XML Literals make the task of working with XML more of a joy than a necessary evil, they do add a certain level of complexity to the language. Any language feature then needs to be maintained moving forward. When I asked Anders about adding them to C#, he pointed to the ongoing maintenance issue along with the supposition that although XML was becoming a de-facto data persistence syntax, would adopting the literals into the language then set a precedence that they would need to support other persistence mechanisms when XML was replaced with some other syntax.

With the rise of REST and decline of SOAP, we have indeed seen the popularity of XML wane in favor of the more compact json syntax. The popularity is increased due to the fact that most JavaScript clients make parsing json into objects trivial. As a result, I have had conversations at conferences joking about the need for json literals in the language as well. At a recent conference an idea came to me which could actually make them (almost) a reality.

At its heart, json is simply a string representation of the JavaScript object structures. These object structures behave much like the .Net dynamic PropertyBag object. As a result, all we really need is a way to embed a long, multi-line string into our VB code and then parse it into a dynamic PropertyBag in order to consume it. Unfortunately, VB doesn’t support multi-line strings. However, it does support multi-line XML. All we need to do is wrap the multi line string inside of an XML element literal:

        Dim jsonLiteral = <js>[
            {author:'Jim Wooley', bookName:'LINQ in Action'},
            {author:'Frank Herbert', bookName:'Dune'},
            {author:'Joe Albahari', bookName:'LINQ Pocket Reference'},
            {author:'Joseph Rattz', bookName:'Pro LINQ'},
            {author:'Charlie Calvert', bookName:'Essential LINQ'}
        ]</js>

With that, we just need to decode the jsonLiteral.Value into a dynamic object. A quick search of Stack Overflow finds a number of handy options for this task. For the sake of this example, I’m just going to use the System.Web.Helpers.Json library that’s part of MVC. We’ll create an extension method to take an XElement and convert it into a dynamic object using the Json.Decode method:

<Extension>
Public Module JsonExtensions
    <Extension>
    Public Function JsonDecode(input As XElement) As Object
        Return Json.Decode(input.Value)
    End Function
End Module

With this in place, we can now operate on the literal just as if it was any other dynamic object type. Here’s the full code for this example.

Option Strict Off

Imports System.Runtime.CompilerServices
Imports System.Web.Helpers

Public Class Test1

    Public Sub TestJsonLiteral()
        Dim jsonLiteral = <js>[
            {author:'Jim Wooley', bookName:'LINQ in Action'},
            {author:'Frank Herbert', bookName:'Dune'},
            {author:'Joe Albahari', bookName:'LINQ Pocket Reference'},
            {author:'Joseph Rattz', bookName:'Pro LINQ'},
            {author:'Charlie Calvert', bookName:'Essential LINQ'}
        ]</js>

        For Each book In jsonLiteral.JsonDecode()
            Console.WriteLine(book.author)
        Next
    End Sub
End Class

<Extension>
Public Module JsonExtensions
    <Extension>
    Public Function JsonDecode(input As XElement) As Object
        Return Json.Decode(input.Value)
    End Function
End Module

I’m sure that there are features that this technique doesn’t cover (including LINQ because Object isn’t directly convertible to IEnumerable). I’m also not sure if this really has any practical benefit. It just is interesting to consider.

Posted on - Comment
Categories: VB - VB Dev Center - Linq to XML -

Windows 8 Live Tiles and LINQ to XML

When I started working with Windows 8 Metro development, I was disappointed to see that the native WinRT API’s relied on the older XmlDocument and XMLDom rather than the newer subset of XDocument/XElement and LINQ to XML. I suppose this was necessary because the older version was more natural for C++ and JavaScript options for Metro applications. If you’re like me and want to use LINQ to XML to work with XML in WinRT, all you need to do is pass the XML strings back and forth between your C#/VB code and the native WinRT methods.

Let’s consider the case of custom Tile Notifications. In order to set one, you first get the XML template from the TileUpdateManager. This returns an XmlDocument object with an XML template looking like this:

<tile>
  <visual>
    <binding template="TileWidePeekImage01">
      <text id="1"></text>
     </binding>
  </visual>
</tile>
In order to set the text, we need to locate the “text” element with the attribute id of 1 and set the value. While we could create the Xml from scratch, It might be safer to just set the node’s value and retain the rest of the XML. If you’ve been following my blog for any time, I find the LINQ to XML API’s to be much easier to use than the older XmlDom. Luckily, moving back and forth from XmlDom and XDocument is as simple as calling Parse on the XDocument passing in the .GetXml method of the XmlDocument. To push our changed XDocument back into the XmlDocument, we call LoadXml passing the String representation of the XDocument using ToString. Here’s the code to grab the TileSquareText04 template and set the text value to “Text Line 1”.
Dim tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquareText04)

' Transition to XDocument
Dim xTile = XDocument.Parse(tileXml.GetXml())
' Manipulate with LINQ to XML
xTile...<text>.FirstOrDefault(Function(node) node.@id = "1").SetValue("Text Line 1")

' Set XmlDocument
tileXml.LoadXml(xTile.ToString())
Dim notification = New TileNotification(tileXml)
TileUpdateManager.CreateTileUpdaterForApplication().Update(notification)

That’s all it takes. If you don’t like this template, there’s plenty more where this came from. MSDN has a great page showing each of the tile samples along with the template XML you need to use.

Posted on - Comment
Categories: Linq to XML - WinRT - VB Dev Center -

Create an RSS Feed for PDC 2010 videos

I love the fact that Microsoft makes it’s conference materials available for those unfortunate enough not to be able to attend. I also love watching the videos on my Zune. Even better is when I can use the Zune podcasting ability to download these videos. So far, I wasn’t able to find such a feed. Thankfully, fellow MVP, Bill McCarthy posted some quick LINQ to XML code to generate HTML tables based on the Xessions XML that was used for PDC. You can read his post here: http://msmvps.com/blogs/bill/archive/2010/11/03/pdc-2010-sessions.aspx.

To take this a step further, I modified his code to generate a quick RSS feed that I can use in the Zune software to download them as if they were a podcast. Here’s the revised code:

Dim doc = XDocument.Load("http://videoak.microsoftpdc.com/pdc_schedule/Schedule.xml")
        Response.Write(<?xml version="1.0" encoding="UTF-8"?>
                       <rss version="2.0">
                           <channel>
                               <title>PDC Videos</title>
                               <link>http://www.Microsoftpdc.com</link>
                               <description>Download content files for PDC 2010.</description>
                               <generator>LINQ</generator>
                               <%= From session In doc...<Session>
                                   From content In session...<Content>
                                   Where content.@Url.EndsWith("Low.wmv")
                                   Select <item>
                                              <title><%= session.<ShortTitle>.Value & " - " & content.@Title %></title>
                                              <link><%= session.@ShortUrl %></link>
                                              <enclosure url=<%= content.@Url %>/>
                                          </item> %>
                           </channel>
                       </rss>)

 

Note: This code does require VB10. If you want to do it with VB9, just add the line continuators (_).

Posted on - Comment
Categories: Linq to XML - VB Dev Center -

LINQ in Action XML samples added to LINQPad

The beginning of this month, we released the samples from "LINQ in Action" for chapters 1-8 which covers LINQ to Objects, LINQ to SQL, and the new Language features. We're happy to announce that the next three chapters covering LINQ to XML are now available. That's over 70 new samples in VB and C# each. Follow the directions on the original announcement to download and use these samples.

Posted on - Comment
Categories: Linq to XML - LinqPad -

More Ways to create an XML file

Edward Tanguay just posted three comparative examples of creating XML files via String Concatenation, XmlWriter, and LINQ to XML. I would typically respond to a blog post through that site's comments, but public comments are blocked at this point. While his post is a good start and shows pretty easily the simplicity that using LINQ to XML gives over the other options, he does fail to address a couple other options:

  • XmlDocument
  • Serialized objects, and of course
  • XML Literals with VB.

Before discussing each of these options, I want to take a minute to address the real issues with the string concatenation sample. While it is relatively easy to use string concatenation to build up the XML, it can be quite dangerous. The main issue is the ease at which you can add invalid characters into the XML elements. In particular, if your variable has one of the characters that need to be escaped (like & or <), using the sample code will cause invalid XML to be generated. You have to properly escape all of the variables which can quickly make the code much more complex. Luckily each of the other XML options handle escaping these values correctly for you automatically.

On to the other options. While you could use the XmlDocument rather than XmlWriter, coding it is much more complex. Also, it doesn't allow for streaming the results out which causes a higher memory footprint as the entire document will be in memory.

Serialized objects give a nice type-safe option, but they do require you to code the object structures before you can consume them. Using tools like XSD.exe or LINQ to XSD helps reduce this overhead, but it is something to be aware of.

I would be remiss if I didn't offer a XML Literal option, so here you go. Here's Edward's C# Sample for reference purposes:

 
XDocument doc = new XDocument(
                new XDeclaration("1.0", null, null),
                new XElement(_pluralCamelNotation,
                    Enumerable.Range(1, 3).Select(
                        i => new XElement(_singularCamelNotation,
                            _allDataTypes.Select(
                                dataType => new XElement(
                                    dataType.CamelCaseNotation,
                                    dataType.GetDummyData())
                            )
                    ))));
            return doc.ToString();

And here's the VB equivalent using XML Literals:


Dim doc = <?xml version="1.0"?>
          <<%= _pluralCamelNotation %>>
              <%= From i In Enumerable.Range(1, 3) _
                  Select <<%= _singluarNotation %>>
                           <%= From t In _allDataTypes _
                               Select <<%= t.CamelCaseNotation %>>
                                           <%= t.GetDummyData %></> %>
                           </> %>
               </>
As it is, we are able to drop a couple lines from the C# code. However, this example is a bit less consise as some XML Literal examples because we are not only inserting the element's values, but the names of the elements themselves dynamically through the literal. As a result there are a number of cases where we have double opening XML tags (<<) which detracts from the readability somewhat. Choosing to use LINQ expressions over Lambda's is purely a matter of style and preference.


        
Posted on - Comment
Categories: Linq to XML -

Fetching XML from SQL Server using LINQ to SQL

With SQL Server, you can use the For Xml clause (read more in BOL). The quickest option is to add For XML Auto at the end of a SQL statement. You can do this with dynamic SQL or inside a stored proc. If you use a stored proc, the DBML tool doesn't recognize this as XML (and return it as an XElement as it does for XML Data type columns).

Regardless of whether you are using stored procs or dynamic SQL, the server returns the result as an array of strings broken up into 4000 character chunks. It is your responsibility to piece this back together. You can concatenate the strings and parse the XML, however there is no true root node in this return set, only a series of XML elements.

Since you are not going to be able to rely on the generated method stub for the procedure, you may want to consider using ExecuteQuery directly and handle the string parsing. If you define this in a partial class for your context, it will appear to calling code as if it came directly from the database pre formatted.  For example, here is some code that returns the customers from Northwind as an XElement:

Public Function CustomerAsXml() As XElement
    Dim returnVal = Me.ExecuteQuery(Of String)("Select * from Customers For XML Auto")
    Dim fullString = String.Concat((From x In returnVal Select x).ToArray)
    Dim xml = XElement.Parse("<root>" & fullString & "</root>")
    Return Xml
End Function

You could substitute the name of your stored proc with parameters in the place of this dynamic SQL and it should work equally well.

Dim returnVal = Me.ExecuteQuery(Of String)("CustomersXml", New Object() {})
 
Posted on - Comment
Categories: LINQ - VB - VB Dev Center - Linq to XML -

Adding a dynamic SiteMap for search engine optimization using LINQ

A couple months ago I added a feature to this site to build a Site Map for this site dynamically based on the information from the database for posts and files for the downloads. If your not familiar with how Sitemap files can help your site searchability, Google has a good documentation about Sitemaps in their Webmaster tools.

The SiteMap Protocal is a rather simple XML document consisting of a set of url nodes that consist of the following:

  • loc - URL for the page link
  • lastmod - Date the page was last modified
  • changefreq - How often the page is changed
  • priority - How high you think the page should be ranked relative to other pages on your site.

For this site, I decided to index the main (default.aspx) File, about and contact pages. In addition, I indexed each post as a separate url node. If you want to view the resulting data, browse to http://www.thinqlinq.com/sitemap.aspx. To do this, I used LINQ to XML with VB XML Literals. To begin, we need to add the XML Namespaces. At the top of our file, we enter the following imports:

Imports <xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
Imports <xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

We continue by adding the root urlset node and one child node representing the main page:

Dim map = _

    <?xml version='1.0' encoding='UTF-8'?>

    <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9

            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"

        xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

 

        <url>

            <loc>http://www.ThinqLinq.com/default.aspx</loc>

            <lastmod>

               <%= (From p In dc.PostItems _

                    Order By p.PublicationDate Descending) _

            .First.PublicationDate.ToString("yyyy-MM-dd") %>

            </lastmod>

            <changefreq>daily</changefreq>

            <priority>1.0</priority>

        </url>

    </urlset>

 

Most of this is standard XML. The main difference is the use of a LINQ query to show the last modification date based on the most recent post from our database. In this case we just want the First date when the dates are ordered descending. We do need to format it properly so that our search engine (Google) will be able to recognize it.

Next up, we need to add the link for the Downloads page. We'll do this much the same way that we added the url for the default page. However, in this case the modification date won't come from the database, but rather use a LINQ to Objects query to get the most recent file in the downloads directory on this site.

<url>

    <loc>http://www.ThinqLinq.com/Files.aspx</loc>

    <lastmod>

        <%= (From f In New System.IO.DirectoryInfo( _

            Server.MapPath("~/Downloads")).GetFiles _

            Order By f.LastWriteTime Descending) _

            .FirstOrDefault.LastWriteTime.ToString("yyyy-MM-dd") %>

    </lastmod>

    <changefreq>weekly</changefreq>

    <priority>1.0</priority>

</url>

The About and Contact pages are relatively straight forward. The remaining url nodes are generated based on the records in the PostItems from our database. To populate them, we'll create a LINQ query pulling the data from the database using LINQ to SQL and projecting (Select) out individual url nodes for each row in the database:

<%= From p In dc.PostItems.ToList _

    Select <url>

               <loc>http://www.ThinqLinq.com/default/<%= p.TitleUrlRewrite %>.aspx</loc>

               <lastmod><%= p.PublicationDate.ToString("yyyy-MM-dd") %></lastmod>

               <changefreq>daily</changefreq>

               <priority>0.3</priority>

           </url> %>


As you can see, there isn't much here that is overly complex. It's just a series of LINQ queries filling the data from various sources. For reference purposes, Here's the complete code:

Imports <xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

Imports <xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

 

Partial Class SiteMap

    Inherits System.Web.UI.Page

 

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Response.Buffer = True

        Response.Clear()

        Response.Cache.SetCacheability(HttpCacheability.NoCache)

        Response.ContentType = "text/xml"

        Response.AddHeader("Content-Disposition", "inline;filename=blog.rss")

        WriteRss()

        Response.End()

    End Sub

 

    Private Sub WriteRss()

        Try

            Using dc As New LinqBlog.BO.LinqBlogDataContext

                Dim map = _

                    <?xml version='1.0' encoding='UTF-8'?>

                    <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"

                        xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

 

                        <url>

                            <loc>http://www.ThinqLinq.com/default.aspx</loc>

                            <lastmod>

                                <%= (From p In dc.PostItems _

                                    Order By p.PublicationDate Descending) _

                                    .First.PublicationDate.ToString("yyyy-MM-dd") %>

                            </lastmod>

                            <changefreq>daily</changefreq>

                            <priority>1.0</priority>

                        </url>

                        <url>

                            <loc>http://www.ThinqLinq.com/Files.aspx</loc>

                            <lastmod>

                                <%= (From f In New System.IO.DirectoryInfo( _

                                    Server.MapPath("~/Downloads")).GetFiles _

                                    Order By f.LastWriteTime Descending) _

                                    .FirstOrDefault.LastWriteTime.ToString("yyyy-MM-dd") %>

                            </lastmod>

                            <changefreq>weekly</changefreq>

                            <priority>1.0</priority>

                        </url>

                        <url>

                            <loc>http://www.ThinqLinq.com/about.aspx</loc>

                            <lastmod>

                                <%= System.IO.File.GetLastWriteTime( _

                                    Server.MapPath("About.aspx")).ToString("yyyy-MM-dd") %>

                            </lastmod>

                            <changefreq>monthly</changefreq>

                            <priority>1.0</priority>

                        </url>

                        <%= From p In dc.PostItems.ToList _

                            Select <url>

                                       <loc>http://www.ThinqLinq.com/default/<%= p.TitleUrlRewrite %>.aspx</loc>

                                       <lastmod><%= p.PublicationDate.ToString("yyyy-MM-dd") %></lastmod>

                                       <changefreq>daily</changefreq>

                                       <priority>0.3</priority>

                                   </url> %>

 

                        <url>

                            <loc>http://www.ThinqLinq.com/Contact.aspx</loc>

                            <lastmod>2008-02-28</lastmod>

                            <changefreq>never</changefreq>

                            <priority>0.1</priority>

                        </url>

                    </urlset>

                Response.Write(map)

            End Using

        Catch ex As Exception

            Response.Write(<error><%= ex.ToString %></error>)

        End Try

 

    End Sub

End Class

Posted on - Comment
Categories: VB Dev Center - LINQ - VB - VS 2008 - SEO - Linq to XML -

Screen scraping and creating Word documents with LINQ to XML

At TechEd Developers 2008 in Orlando, I had the pleasure of competing in Speaker Idol. In that competition, we had the opportunity to present a topic in 5 minutes. Unfortunately, the topic I choose really needed 10 minutes to cover at the level of detail it needed. Instead of limiting the topic, I decided to go ahead and present it a bit too fast.

If you want to see the video, or see how to use VB 9's XML Literals and LINQ to XML to fetch data from a web page (that must be XHtml compilant), manipulate it and insert it into a Word 2007 file, it is now available on the  Developer Landing page, and the Library page. of the TechEd site. If you prefer, you can jump right to the video in either WMV or MP4 file formats. If you're not familiar with LINQ to XML, go ahead and download the video and just watch it at half speed ;-)

Posted on - Comment
Categories: VB Dev Center - LINQ - VB - Linq to XML -

Querying the complete plays of Shakespeare using LINQ to XML

I was working to come up with some creative uses of LINQ to XML for my new talk I'm giving at the Huntsville, AL Code Camp. I figured it would be good to include a sample which queries a large XML document. Remembering that the complete works of Shakespeare were available in XML form, I did a quick search and found a version at http://metalab.unc.edu/bosak/xml/eg/shaks200.zip. This file separates each play out into separate XML files. Since I wanted to find out which parts had the most lines across all plays, I wanted to put them into a single XML file. Rather than doing this manually, I went ahead and whipped up a quick LINQ query to fetch the xml documents and load them up into an array of XElements:

Dim plays = _
    From file In New System.IO.DirectoryInfo("C:\projects\ShakespeareXml").GetFiles() _
   
Where file.Extension.Equals(".xml", StringComparison.CurrentCultureIgnoreCase) _
   
Let doc = XElement.Load(file.FullName) _
   
Select doc

Ok, now that out of the way, I really wanted to load up a single XML file with these resulting nodes. Pretty easy using XML Literals. Just wrap the query with a new root element:

Dim plays = _
  
<Plays>
   
<%= From file In New System.IO.DirectoryInfo("C:\projects\ShakespeareXml").GetFiles() _
   
Where file.Extension.Equals(".xml", String Comparison.CurrentCultureIgnoreCase) _
   
Let doc = XElement.Load(file.FullName) _
   
Select doc %>
  
</Plays>

Easy. Now I have a new XML document containing the complete plays of Shakespeare. Now, what can we do with it... Well, we can get a count of the plays in one line:

Console.WriteLine("Plays found: " & plays.<PLAY>.Count.ToString)

We could have done that without putting it into a new document. We do see that we have 37 plays represented, so we know the first query worked. Now, to count the number of lines (LINE) for each character (SPEAKER). The XML document groups each set of lines into a parent node called SPEECH. This SPEECH node then contains the SPEAKER element and a series of LINE elements. For example, here's the beginning of Juliet's fameous Romeo, Romeo speech:

<SPEECH>
   
<SPEAKER>JULIET</SPEAKER>
   
<LINE>O Romeo, Romeo! wherefore art thou Romeo?</LINE>
   
<LINE>Deny thy father and refuse thy name;</LINE>
   
<LINE>Or, if thou wilt not, be but sworn my love,</LINE>
   
<LINE>And I'll no longer be a Capulet.</LINE>
</SPEECH>

So to achieve the goal of counting our lines by character, we find the descendent nodes of the plays element (plays...<SPEECH>) and group them by the speaker. Then we project out the name of the speaker and the number of lines they have. We don't care about the bit roles, so we'll order the results in descending form based on the number of lines (LineCount). We'll limit the results to the top 50 entries. Here's the resulting query:

Dim mostLines = _
  
From speech In plays...<SPEECH> _
  
Group By key = speech.<SPEAKER>.Value Into Group _
  
Select Speaker = key, _
            LineCount =
Group.<LINE>.Count _
  
Order By LineCount Descending _
  
Take 50

The amazing thing with this process, running all three queries here, including the one which loads the full XML from the various files takes less than a second. I haven't had time to do a full performance test, including memory load, but the initial results are quite impressive!

If you have other creative uses of LINQ to XML, let me know, I'd love to include them in future presentations. Also, if you're in the Huntsville, AL area on 2/23/2008, head on over to the code camp and see the entire presentation in person.

Posted on - Comment
Categories: VB Dev Center - LINQ - VB - Linq to XML -

Adding categories to the RSS feed using LINQ to XML

Last time, we added categories to the web view of the ThinqLinq site. This time, we're going to add them to the RSS feed. Because RSS is "Really Simple", adding the categories is fairly easy. According to the RSS specification, <category> is an optional sub-element of <item>. It can additionally contain the domain that contains that category. In our case, we will point the domain to our implementation that displays all posts for a given category by passing the category id to the query string of our default page. Thus we would want to insert a category like follows:

<category domain="http://www.ThinqLinq.com/default.aspx?CategoryId=1">LINQ</category>

To refresh your memory, we created last discussed creating RSS in the following post: http://www.thinqlinq.com/Default/Projecting_XML_from_LINQ_to_SQL.aspx. To add our comments, we need to inject an additional query before inside of each <item> element for the categories. To generate each of the categories for a post, we add a query to project (using the select statement) our new <category> nodes as follows:

     <%= From c In post.CategoryPosts _
              Select <category domain=<%= "Default.aspx?CategoryId" & c.CategoryID %>>
             
<%= c.Category.Title %></category> %>

Notice here, when we are creating the domain, we can use the CategoryID of the CategoryPosts object from the many-to-many table. To display the actual post's title, we need to drill through the CategoryPost to the child Category object's Title property (c.Category.Title). Putting this all together, Using the magic of LINQ to XML and VB, we arrive at the following statement:

Response.Write(<?xml version="1.0" encoding="UTF-8"?>
 
<rss version='2.0' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:slash='http://purl.org/rss/1.0/modules/slash/' xmlns:wfw='http://wellformedweb.org/CommentAPI/'>
 
<channel>
   
<title>Thinq Linq</title>
   
<link>http://www.ThinqLinq.com/default.aspx</link>
   
<description>Thoughts relating to LINQ and Language Integrated Query related topics.</description>
   
<dc:language>en-US</dc:language>
   
<generator>LINQ</generator>
   
<%= From post In query.ToArray _
            Select <item>
                        
<title><%= post.Title %></title>
                         <link><%= "http://www.thinqlinq.com/Default.aspx?Postid=" & post.Id.ToString %></link>
                         
<pubDate><%= post.PublicationDate.ToString("ddd, dd MMM yyyy hh:mm:ss GMT") %></pubDate>
                        
<guid isPermaLink="false">42f563c8-34ea-4d01-bfe1-2047c2222a74:<%= post.Id %></guid>
                        
<description><%= post.Description %></description>
                        
<%= From c In post.CategoryPosts _
                                  Select <category domain=<%= "Default.aspx?CategoryId" & c.CategoryID %>>
                                 
<%= c.Category.Title %></category> %>
                        
</item> %>
   
</channel>
  </rss>)

If you want to view the resulting feed, browse directly to http://www.thinqlinq.com/rss.aspx rather than using the standard RSS feed link which is managed through FeedBurner to allow me to gather additional statistics for this site.

Posted on - Comment
Categories: VB Dev Center - LINQ - VB - Linq to XML -

Adding a RSS feed for file downloads

Ok, so I've been a bit busy this weekend adding some nice stuff for this site. One thing that I wanted to add was another RSS feed, this time for the file upload section. If you want to subscribe to the File RSS feed, direct your aggregator to the following link: http://www.thinqlinq.com/Files.aspx/Rss

Of course, since this is a learning site, I'll let you in on the code needed to accomplish the task. As you may guess, LINQ makes serving up XML from an object collection using a heterogeneous join to a database fairly easy. If you haven't seen how we display the files for this site, check out the podcast I did for Wally last year where we go over it in more detail.

To begin the task, we will set-up our initial query We will use two data sources for this task. The first data source is the object collection containing the FilInfo objects from the download directory for this site. (System.IO.DirectoryInfo(...).GetFiles). The second source is the FileClassic table returned using LINQ to SQL. LINQ allows us to easily join these two data sources and work with it as we would any other data source.

Dim fileDescriptions = dc.GetTable(Of FileClassic)()
Dim files = (From f In New System.IO.DirectoryInfo(Server.MapPath("~/Downloads")).GetFiles _
                  Join desc In fileDescriptions On f.Name Equals desc.FileName _
                  Order By f.LastWriteTime Descending _
                  Select URL = "Downloads/" & f.Name, _
                  Name = System.IO.Path.GetFileNameWithoutExtension(f.Name), _
                  f.LastWriteTime, f.Length, _
                  Description = Server.HtmlDecode(desc.Description), desc.Id).ToArray

With our query prepared, we can now use LINQ to XML with VB Literals to generate the RSS feed and output it to the response stream as follows:

Response.Write(<?xml version="1.0" encoding="UTF-8"?>
 
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
    <channel>
    <title>ThinqLinq samples and presentations</title>
    <link>http://www.ThinqLinq.com/Files.aspx</link>
    <description>Here are some of my presentation files available for you to use as you wish.  Just let me know if you like what you see and what you want to see more of.</description>
   
<%= From f In files _
            Select <item>
                       
<title><%= f.Name %></title>
                       
<link><%= "http://www.ThinqLinq.com/" & f.URL %></link>
                       
<pubDate><%= f.LastWriteTime.ToString("ddd, dd MMM yyyy hh:mm:ss GMT") %></pubDate>
                       
<guid isPermaLink="false"><%= "42f563c8-34ea-4d01-bfe1-2047c2222a74:" & f.Id %></guid>
                       
<description><%= f.Description %></description>
                      
<enclosure url=<%= "http://www.ThinqLinq.com/" & f.URL %>
                                        
length=<%= f.Length %>
                                         type="application/x-zip-compressed"
/>
                     
</item> %>
   
</channel>
 
</rss>)

The main difference between this example and the ones we are using for blog posts is the addition of the <enclosure> node. With enclosure, we specify the url to the file download, the file length, and the file type. In this site, I plan to always use ZIP files so I just hard code that. The other values come from our starting query. If you subscribe to this new feed, you should see each item including an enclosure icon so that you can download it.

Posted on - Comment
Categories: VB Dev Center - LINQ - VB - Linq to XML -

Projecting XML from LINQ to SQL

Among the new cool features in Visual Studio 2008, one of the best may be the XML Literal support with VB 9 and LINQ. In my last post, I mentioned some changing features from the Beta to RTM. One that could easily be overlooked is a change to the way LINQ to SQL can now directly project into XML literals.

Through the Beta cycle, there was an issue with projecting XML elements directly from a LINQ to SQL query. If you haven't seen LINQ to SQL with XML, here's a code sample that explains what I'm referring to:

Dim dc As New LinqBlogDataContext
'Formulate the Query to get the last 10 blog posts
Dim query = (From p In dc.PostItems _
                     
Order By p.PublicationDate Descending _
                     
Take 10 _
                      
Select p).ToArray

'Create a root Site node with 10 child "item" nodes.
'Each node will be filled in through a LINQ query
Dim fooShort = _
 
<site>
       
<%= From p In query _
                
Select _
            
<item>
                 
<title><%= p.Title %></title>
                 
<link>http://ThinqLinq.com/Default.aspx?Postid=<%= p.Id %></link>
                 
<pubDate><%= p.PublicationDate %></pubDate>
                 
<guid isPermaLink="false">42f563c8-34ea-4d01-bfe1-2047c2222a74:<%
p.Id %></guid>
                 
<description><%= p.Description %></description>
                
</item> %>
 
</site>

In this code, I'm performing two queries. The first one sets up the LINQ to SQL query and pre-fetches the results into an Array. In the beta builds, if we didn't include the pre-fetching ToArray, the second query which projects the results of the first into individual <item> nodes. What is the difference between these queries? The first query uses LINQ to SQL and projects results directly from the database. Because we pre-fetch the results into an array of objects, the resulting query only uses LINQ to Objects rather than the direct LINQ to SQL implementation.

With the final RTM of Visual Studio, we no longer need to pre-fetch the results from the query. Instead, we can directly project our desired XML from the select statement without needing the intermediary step. Here is the revised code. Notice, we can now perform the same result with a single LINQ query rather than two.

Dim fooNew = _
 
<site>
      
<%= From p In dc.PostItems _
                Order By p.PublicationDate Descending _
               
Take 10 _
                
Select _
           
<item>
               
<title><%= p.Title %></title>
               
<link>http://ThinqLinq.com/Default.aspx?Postid=<%= p.Id %></link>
               
<pubDate><%= p.PublicationDate %></pubDate>
               
<guid isPermaLink="false">42f563c8-34ea-4d01-bfe1-2047c2222a74:<%= p.Id %></guid>
               
<description><%= p.Description %></description>
           
</item> %>
   
</site>

The result is more concise. You may find you want to continue separating your query definition from your XML creation in order to improve maintainability. If this is the case, simply keep the first code sample and remove the call to .ToArray. Because LINQ to SQL is composable, you can separate the queries into two code sets. When the query is evaluated, the two expressions will be combined into a single query to the database and the projection will continue to work.

Enjoy working with VB 9 and XML. In my opinion it is one of the killer features of Visual Studio 2008. If you give it a try, I think you might find the same.

Posted on - Comment
Categories: VB - LINQ - Linq to XML -

Creating HTML emails using VB 9 and LINQ

Today, I'm not looking at sending mass spam using LINQ to pull a list of recipients. I'm actually referring to the ability to generate the message body using XML Literals. Using the System.Net.Mail.MailMessage object, we can easily send emails to an SMTP server.

The body of the email can either be plain text or HTML. Dynamically generating the text is often a laborious task involving a string builder and lots of method calls. The body corresponds to the body portion of a HTML page. If you use well formed XHTML in the body, you are actually generating a specialized version of XML. Once we are working with XML, we can use XML Literals in VB to format our output.

I recently had to do this on a project to send lists of updated values from an external source. In the body, I needed to dynamically fill a HTML table with the new values. The table consists of 4 columns: State, County, Limit, Effective Date. I begin by laying out the content in a HTML editor (like Visual Studio 2008...) Here's the results:

<body>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur eros purus, suscipit ac, pulvinar vel, aliquet vehicula, pede. Duis eros dolor, iaculis non, aliquam sed, tincidunt ac, diam.
   
<table>
     
<tr><th>State</th>
           
<th>County</th>
           
<th>New Limit</th>
           
<th>Effective Date</th>
     
</tr><tr><td>XX</td>
                     
<td>Foo</td>
                    
<td>$123</td>
                     
<td>1/1/2000</td>
               
</tr>
   
</table>
 
</body>

I know what you must be thinking by now: Gee Jim, how could you come up with such a beautiful page. As Bones would say, "D@mmit Jim, I'm programmer not a designer." We'll keep it clean for now to focus on what is yet to come.

Realize that our body tag is actually the root of a well-formed XML document. As such, we can copy it as a template directly into our tool (which is a console application by the way), add a reference to System.Linq and System.Xml.Linq, and paste it into our VB module assigning a variable, let's call it "body" to the XML.

While we're at it, we'll go ahead and insert new rows into the table based on the results of an object query. In this query, we'll iterate over the records we are adding which is an IEnumerable(Of Limit). We'll project a new row (<tr>) for each object in our iteration. Rather than imperatively iterating, we'll use LINQ's declarative syntax In addition, we'll insert our values using the <%= %> place holders. Here's the resulting declaration:

Friend Shared Sub SendUpdate(ByVal newItems As IEnumerable(Of FhaLimit))
Dim body = _
  <body>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur eros purus, suscipit ac, pulvinar vel, aliquet vehicula, pede. Duis eros dolor, iaculis non, aliquam sed, tincidunt ac, diam. 
    <table>
     
<tr><th>State</th>
           
<th>County</th>
           
<th>New Limit</th>
           
<th>Effective Date</th>
     
</tr><%= From limit In newItems _
                      Order By limit.State, limit.CountyName _
                      Select <tr><td><%= limit.State %></td>
                                      
<td><%= limit.CountyName %></td>
                                      
<td><%= limit.Units1.Value.ToString("c0") %></td>
                                      
<td><%= limit.LimitTransactionDate.ToShortDateString %></td>
                               
</tr> %>
   
</table>
 
</body>

If you've coded ASP.NET, the resulting declaration should look very familiar. Realize that this is being done in a VB module in a console application. We are not coding in a .ASPX file! The resulting maintainability of this code is much better than the old way using string builders or XSLT.

To finish off the task, we are going to send the message with our new XHTML body. This is very easy with .NET as well.

Dim message As New System.Net.Mail.MailMessage("from@ThinqLinq.com", "to@ThinqLinq.com", "Limits Updated", body.ToString)
message.IsBodyHtml = True
Dim server As New System.Net.Mail.SmtpClient(My.Settings.SmtpServer)
server.Send(message)

There you go, a quick and painless way to create HTML emails using VB 9 and LINQ. Let me know what you Thinq.

Posted on - Comment
Categories: LINQ - VB - VB Dev Center - Linq to XML -

Using LINQ to query against objects and XML

In preparing for my LINQ talk for DBA's at tomorrow's Atlanta Microsoft Database Forum user group meeting, I thought it would be good to offer a quick look at the LINQ to Objects and LINQ to XML stacks before focusing on the OR mapping portions: LINQ to SQL,  LINQ to Entities and LINQ to dataSet. I previously used a quick demo which joined  the FileInfo collection returned by DirectoryInfo.GetFiles with an object collection built from a CSV file. For this demo, I wanted to add in the XML stack. Making this change is a relatively minor task.

First, I converted the CSV file to a simple XML document. See my previous post to see how straight-forward the conversion is. Once converted, our XML looks like the following:

<FileTypes>
   <FileType extension=".doc" description="Microsoft Word Document" />
   <FileType extension=".log" description="Log File" />
   <FileType extension=".msg" description="Mail Message" />
   <FileType extension=".rtf" description="Rich Text Format" />
   <FileType extension=".txt" description="Text File" />
   <FileType extension=".wpd" description="WordPerfect Document" />
</FileTypes>

To query this, we need to first load the XML document. LINQ to XML offers a single line method to load a XML document using the simple "Load" method.

Dim FileTypes As XDocument = XDocument.Load("C:\FileTypes.xml")

Once we have the document, we can easily get a collection of elements using the .Descendents("FileType") method. VB 9 offers a more concise method which retains a more type safe mechanism. Here we identify the specific nodes we want to access (FileTypes.<FileTypes>.<FileType>). With this collection in hand, we can replace the old CSV based collection with the new XML collection using the following LINQ query.

Dim results = _
From fi In New DirectoryInfo("C:\Windows").GetFiles, _
ext In FileTypes.<FileTypes>.<FileType> _
Where fi.LastAccessTime >= Now.AddDays(-6) _
And fi.Extension = ext.Attribute("extension").Value _
Select New {FileName := fi.Name, lastAccess := fi.LastAccessTime, Exten := ext.Attribute("description").Value} _
Order By FileName Descending

Posted on - Comment
Categories: LINQ - Linq to XML -

Use LINQ to convert CSV to XML

File conversion is a typical programming task. VB9 should make this task easier to code and maintain as we can eliminate much of the plumbing code and focus on the task at hand. The following example uses the MAY CTP of LINQ and leverages the TextFieldParser added to VB 8 to convert a simple CSV file to XML. For this sample, we will convert a simple CSV file that contains standard windows file extensions with a brief description of the file. Here are the first couple of lines:

.doc,Microsoft Word Document
.log,Log File
.msg,Mail Message
.rtf,Rich Text Format
.txt,Text File

For this task, I want to create an XML document with a single root node and multiple child nodes with the extension and description as attributes of a FileType element as follows:

<FileTypes>
   <FileType extension=".doc" description="Microsoft Word Document" />
   <FileType extension=".log" description="Log File" />
   <FileType extension=".msg" description="Mail Message" />
   <FileType extension=".rtf" description="Rich Text Format" />
   <FileType extension=".txt" description="Text File" />
   <FileType extension=".wpd" description="WordPerfect Document" />
</FileTypes>

To accomplish this, we need to do 3 simple steps: iterate through the csv file, Add the data to our XML document, save the document to disk. First, here is the complete code. We will examine each part below.

Public Shared Sub CsvXml(ByVal sourceFile As String, ByVal destinationFile As String)

   'Create a base XML element called FileTypes
   Dim value As New XElement("FileTypes")

   'Create a new delimited file reader using VB 2005's TextFieldParser
   Using reader As New Microsoft.VisualBasic.FileIO.TextFieldParser(sourceFile)
      reader.Delimiters = New String() {","}

      'Read data from the CSV
      While Not reader.EndOfData
         Dim currentRow() As String = reader.ReadFields

         'Use XLINQ to add the child elements to the base element
         value.Add(<FileType extension=<%= currentRow(0) %> description=<%= currentRow(1) %>/>)
      End While
   End Using

   'Save it
   value.Save(destinationFile)
End Sub

With LINQ to XML changes the model for XML from a document centric model to an Element centric model, thus to begin working with XML, we simply need to create a new element

   Dim value As New XElement("FileTypes")

Next, we need to open and read the lines of the CSV file. VB 2005 added a helpful class called the TextFieldParse which takes a stream, uri, or TextReader. To be simple, we will just use a string for the uri of the file we want to open. We also need to identify the kind of delimiter the CSV file uses. In this case, we are using a comma delimited file, so we will add a "," as the array of delimiters. In addition, we will leverage the new "Using" keyword to make sure we dispose of our file once we are done with it, even if we error while iterating through the file.

   Using reader As New Microsoft.VisualBasic.FileIO.TextFieldParser(sourceFile)
      reader.Delimiters = New String() {","}

To read each line, we set up a loop to continue iterating until we are out of data. We get the actual values for each line by calling the ReadFields method of the TextFieldParser.

      'Read data from the CSV
      While Not reader.EndOfData
         Dim currentRow() As String = reader.ReadFields

Now that we have the data we want to work with we can leverage the new functional construction available in LINQ to XML to add new elements. Our element name is FileType and has two attributes--extension and description. Using the <%=  ... %> syntax familiar to ASP or CodeSmith users we can easily insert the values from our CurrentRow array of values into the appropriate locations

         'Use XLINQ to add the child elements to the base element
         value.Add(<FileType extension=<%= currentRow(0) %> description=<%= currentRow(1) %>/>)
      End While
   End Using

Perhaps the easiest part of the process is to save the resulting XML element. LINQ to XML includes a simple Save method on the element which saves the element and all of it's children with a simple call.

   value.Save(destinationFile)
End Sub

Naturally, this sample could be extended easily to be more flexible. In particular, taking a CSV file with the names of the fields in the first row and dynamically building an XML document based on this CSV would be a helpful extension which I may try to demonstrate when I have more time.

Posted on - Comment
Categories: LINQ - Linq to XML -

Behind LINQ to XML and the Event Log generator

Yesterday, I posted my "cool code" example which extracted information from the event logs and presented them as a RSS document using Linq to XML (XLINQ). At that point, I didn't have time to explain the code. Today, I will deconstruct the code to help explain how it works.

As a recap, here's the "one line of code" that handles the task:

If EventLog.Exists(LogName) Then _
Response.Write(<channel>
<title>Event Log from <%= Environment.MachineName %></title>
<description>Events logged in the system's <%= LogName %> Event Log</description>
<copyright>This RSS feed is copyright (c) <%= Now.Year.ToString %>.</copyright>
<publisher>Your Name</publisher>
<author>you Lastname</author>
<language>en-US</language>
<%= From entry In New EventLog(LogName).Entries.Cast(Of EventLogEntry)() _
Where (FilterType Is Nothing OrElse FilterType.ToUpper = entry.EntryType.ToString.ToUpper) And _
(EventSourceName Is Nothing OrElse EventSourceName.ToUpper = entry.Source.ToUpper) _
Select <item>
<title><%= entry.EntryType.ToString() & ": " & entry.Source %></title>
<guid isPermaLink="false"><%= Environment.MachineName & "/" & LogName & "/" & entry.Index %></guid>
<description><%= GetItemText(entry) %></description>
<pubDate><%= entry.TimeWritten.ToUniversalTime().ToString() %></pubDate>
<date><%= entry.TimeWritten.ToString() %></date>
<creator><%= entry.UserName %></creator>
</item> %>
</channel>)

To begin understanding what is going on, let's take a look at the XML literals in VB9. The main advance for XML in the next version of VB (and why VB 9 is cooler than C# 3.0) is the ability to declaritivly create XML elements using inline literals. Thus we can simply create an XML document in VB 9 with the following syntax:

Dim foo as XElement = _
<parent>
<child>this is some content</child>
</parent>

Given this we can easily start constructing the RSS envelope as follows:

Dim rss as XElement = _
<channel>
<title></title>
<description></description>
<copyright></copyright>
<publisher></publisher>
<author></author>
<language></language>
</channel>

With that format, we simply insert our content inline inside of the elements as normal. If we want to perform some code action and insert function results or variables inside of an element, we simply isolate it with the <%= %> tags familiar to ASP.Net developers. Thus we can include the Machine's name as part of the title element using the following syntax:

<title>Event Log from <%= Environment.MachineName %></title>

VB 9 doesn't limit us to simple function calls, but also allows us to declare procedural looping or other code segments, including LINQ queries. In this way, we are able to create our <item> collection by iterating through the list of events. Since LINQ allows us to easily iterate through list elements, it is the perfect vehicle for declarativly creating our items. Thus with LINQ, we would be able to use the following query:

From entry In New EventLog(LogName).Entries.Cast(Of EventLogEntry)() _
Where (FilterType Is Nothing OrElse FilterType.ToUpper = entry.EntryType.ToString.ToUpper) And _
(EventSourceName Is Nothing OrElse EventSourceName.ToUpper = entry.Source.ToUpper) _
Select entry

In our code, we have acquired the FilterType and EventSourceName values from the query string. (See the code download for the implementation details). One detail that should be mentioned here relates to the .Cast extension method. LINQ query operators are able to work on any object that implements IEnumerable(Of T). However, the EventLog(LogName).Entries object only implements IEnumerable rather than the strongly typed generic version. Luckily with the MAY CTP, they added the Cast(Of T) extension method which converts the Untyped IEnumerable into a strongly typed IEnumerable(Of T). With this method, we can create an IEnumerable(Of EventLogEntry) rendition from the .Entries list.

Now that we can iterate over our collection, we need to project the results into XML Element nodes (XElements). This is just as easy to do as it is to create the RSS envelope earlier. Here for our select projection, we replace the simple "Select entry" with a more complex XML projection:

Select <item>
<title><%= entry.EntryType.ToString() & ": " & entry.Source %></title>
<guid isPermaLink="false"><%= Environment.MachineName & "/" & LogName & "/" & entry.Index %></guid>
<description><%= GetItemText(entry) %></description>
<pubDate><%= entry.TimeWritten.ToUniversalTime().ToString() %></pubDate>
<date><%= entry.TimeWritten.ToString() %></date>
<creator><%= entry.UserName %></creator>
</item>

Once again, we are able to inject the results of CLR functions, including Environment.MachineName; or our custom methods like GetItemText(entry). Into child nodes. Additionally, by placing our entire query (from...select) inside of the <%= %> region we can easily insert all of the <item> elements inside the body of our <channel> node.

Once we have our XML item constructed, it is extremely easy to serve the resulting XML up to a consumer. All we need to do is call the .ToString method on the XElement. Since it is the default, we can simply just Response.Write out the results of the XLINQ created document and walla, an RSS feed of the event logs in "one [REALLY LONG] line of code."

Posted on - Comment
Categories: Linq to XML -

Using LINQ to XML to generate a RSS feed from the event log

The Jacksonville Code Camp tried a new concept which was very well received. In it they asked people to bring "cool code" and show it in 7 minutes. Personally, I'm not sure what can be cooler than VB 9's XML literals, so I decided to give it a shot. In the end, I came up with a ONE LINE solution which can iterate over the event log and generate an XML string in the format of a RSS feed which is served up to the Response stream. When I showed the one line, some were skeptical. Granted, there are a couple of line continuation characters, but it is indeed a single function call (which happens to take up 18 lines when formatted for readability...) Anyway, here's the one line:

Private Sub WriteRss()
If EventLog.Exists(LogName) Then _
Response.Write(<channel>
<title>Event Log from <%= Environment.MachineName %></title>
<description>Events logged in the system's <%= LogName %> Event Log</description>
<copyright>This RSS feed is copyright (c) <%= Now.Year.ToString %>.</copyright>
<publisher>Your Name</publisher>
<author>you Lastname</author>
<language>en-US</language>
<%= From entry In New EventLog(LogName).Entries.Cast(Of EventLogEntry)() _
Where (FilterType Is Nothing OrElse FilterType.ToUpper = entry.EntryType.ToString.ToUpper) And _
(EventSourceName Is Nothing OrElse EventSourceName.ToUpper = entry.Source.ToUpper) _
Select <item>
<title><%= entry.EntryType.ToString() & ": " & entry.Source %></title>
<guid isPermaLink="false"><%= Environment.MachineName & "/" & LogName & "/" & entry.Index %></guid>
<description><%= GetItemText(entry) %></description>
<pubDate><%= entry.TimeWritten.ToUniversalTime().ToString() %></pubDate>
<date><%= entry.TimeWritten.ToString() %></date>
<creator><%= entry.UserName %></creator>
</item> %>
</channel>)

End Sub

I acknowledged to Vinay, that I would accept that this could be considered as 18 lines, but still think it is way cool code. Evidently, I wasn't alone as the judges awarded it 3rd place and it won the audience's choice as coolest code. If you want the complete code (less than 100 lines of code including comments) it is available for download as well.

My original intention was to extract and serve emails, but writing the POP3 or MAPI wrapper would have pushed the solution out more than I wanted for the purpose of this contest. It would be easy to convert this code to serve those purposes as well.

Posted on - Comment
Categories: Linq to XML - Code Camp -

LINQ to XML Rocks Because

I’m in the process of finalizing a number of presentations on LINQ. I will be posting the demos and slides soon. As a teaser, here’s a quick snippet from the XLinq demo. I begin by showing the new functional construction syntax for XLINQ.

'Uses Functional Construction
Dim x As New XElement("LinqEssentials", _
New XElement("Category", "LINQ"), _
New XElement("Category", "DLINQ"), _
New XElement("Category", "XLINQ"))
Dim y As New XElement("LanguageFeatures")
y.Add(New XElement("release", New XAttribute("version", "2.0"), _
New XElement("feature", "Generics"), _
New XElement("feature", "Iterators"), _
New XElement("feature", "Anonymous Delegates"), _
New XElement("feature", "Nullable Types") _
))
y.Add(New XElement("release", New XAttribute("version", "3.0"), _
New XElement("feature", "Implicit Types"), _
New XElement("feature", "Extension methods"), _
New XElement("feature", "Lambda Expressions"), _
New XElement("feature", "Object initializers"), _
New XElement("feature", "Anonymous Types"), _
New XElement("feature", "Expression Trees") _
))
y.Add(New XElement("release", New XAttribute("version", "9.0"), _
New XElement("feature", "dynamic interfaces"), _
New XElement("feature", "dynamic identifiers"), _
New XElement("feature", "XML Literals") _
))
Dim z As New XElement("XLinqRocksBecause")
z.Add(x)
z.Add(y)

Debug.Write(z)

While it is elegant and lets you work more directly with the document structure, the c# method is not quite as cool as the VB one. In the demo, I run the first code snippet, then copy the output from the debug.write method which sends out the XML nicely formatted. Then you create a new variable and paste in the output from the debug.write call as follows:

Dim x = <VBXLinqRocksBecause onDate=<%= Today %>>
    <LinqEssentials>
        <Category>LINQ</Category> 
        <Category>DLINQ</Category> 
        <Category>XLINQ</Category> 
    </LinqEssentials> 
    <LanguageFeatures> 
        <release version="7.0"> 
            <feature>Generics</feature> 
            <feature>Iterators</feature> 
            <feature>Anonymous Delegates</feature> 
            <feature>Nullable Types</feature> 
        </release> 
        <release version="8.0"> 
            <feature>Implicit Types</feature> 
            <feature>Extension methods</feature> 
            <feature>Lambda Expressions</feature> 
            <feature>Object initializers</feature> 
            <feature>Anonymous Types</feature> 
            <feature>Expression Trees</feature> 
        </release> 
        <release version="9.0"> 
            <feature>dynamic interfaces</feature> 
            <feature>dynamic identifiers</feature> 
            <feature>XML Literals</feature> 
        </release> 
    </LanguageFeatures> 
</VBXLinqRocksBecause> 

Notice, there are no quotes around any of this. Accessing an element can be as simple as the following (note, you will need to turn Option Strict OFF for this one to work).:

Console.WriteLine(CStr(x.@onDate)

While I think LINQ and XLINQ are cool, I must applaud the VB/XML/DATA teams for their efforts on this one. Finally XML in VB ROCKS!

If you want to learn more, head on over to the LINQ center on MSDN, or come to my talks at the Alabama Code Camp, Atlanta Dotnet User Group, or Atlanta Code Camp over the next few weeks.

Posted on - Comment
Categories: Linq to XML - VB -