Delen via


Using LINQ to XML with Azure Tables

In a previous post, I implemented the Azure Table REST APIs as a starting point for testing without having to use the Storage Client sample that ships in the Azure SDK. The Azure Storage REST API is documented here. Instructions and the updated code that demonstrates processing Azure table data with LINQ to XML are at the bottom of this post.

Version 3

After using the original code in a few prototype projects, I very quickly got tired of writing XML parsing code. I tried several different methods, including using LINQ to XML. After getting the code debugged, LINQ to XML is definitely my preferred approach for parsing XML data.

Getting LINQ to XML Working

Step one to getting LINQ to XML to work on the Azure table data is adding the two XML Namespaces that correspond to the "m:" and "d:" prefixes on the properties and data elements in the XML.

XNamespace xnD = "https://schemas.microsoft.com/ado/2007/08/dataservices";
XNamespace xnM = "https://schemas.microsoft.com/ado/2007/08/dataservices/metadata";

The next step is to get the results into an XDocument so that LINQ can parse the data. It is very simple to use the HttpWebResponse ResponseStream as the source for the XDocument.

using (HttpWebResponse res ...

XDocument xDoc = XDocument.Parse(new StreamReader(res.GetResponseStream()).ReadToEnd());

Now that we have an XDocument, we can write LINQ queries to process the data. For this example, assume the simple Contact class. Notice that there is nothing special about this class, it's just an entity class.

public class Contact

{

public string FirstName { get; set; }

public string LastName { get; set; }

public string Phone { get; set; }

}

This LINQ query will get a list of all of the contacts from the XDocument:

var list = (from properties in xDoc.Descendants(xnM + "properties")

select new Contact

{

FirstName = XConvert.ToString(properties.Element(xnD + "FirstName")),

LastName = XConvert.ToString(properties.Element(xnD + "LastName")),

Phone = XConvert.ToString(properties.Element(xnD + "Phone")),

});

Notice the XML Namespace qualifiers.

Also notice the call to XConvert.ToString(). XConvert is a helper class that I wrote to handle the results from LINQ. It is much like the built-in Convert class in that it has static methods for converting to a variety of types. The big difference is that it takes an XElement as a parameter.

It turns out that simply checking Element("foo").Value for null isn't sufficient. If the source XML doesn't contain the element (i.e. a version change on the data), Element is actually null. The XConvert methods check that Element is not null and that Element.Value is not null. If either of these are null, the method returns the default value (string.empty). For non-string types, the methods use TryParse to convert the values and return the value or the default depending upon success. The XConvert class is included in the sample code.

You can use a foreach loop to process the items in the list just like you would any IEnumerator.

I like to use the generic List<T> data structure within my apps. It turns out that it is very easy to do this:

List<Contact> ContactList = list.ToList<Contact>();

You can also have LINQ create the strongly typed list for you. Replace the "var" on the first line of the query with the type (in this case, List<Contact>). On the last line, add the .ToList<Contact>() between the "})" and ";".

To determine if there are any results, you can use:

list.Any<Contact>()

To get the first element in the list

if (list.Any<Contact>())

{

Contact person = list.First<Contact>();

}

This is helpful when you're only retrieving a single item from the query vs. a list of items.

Although not related to Azure, LINQ also lets you easily sort and filter lists in memory.

Sort:

var sortedlist = from c in list

orderby c.LastName descending, c.FirstName descending

select c;

Filter:

var filteredlist = from c in ContactList

where c.FirstName == "Jane"

select c;

I expect to use SDS for most Azure data storage needs, but when I use Azure tables, I will probably continue to call the REST interface and process with LINQ.

Caveats

· The code still only works on Azure Storage, it does not work on development storage (see my reasoning in previous posts)

· The updated code is sample code quality with limited error handling, testing, and comments, so it’s only appropriate for experimentation and as a starting point. If you find bugs, please let me know.

· The code is still all in one file, which is easy to post and get running the first time, but not a best practice for a real app.

· You still need to update the account / secret information in the RESTHelper class.

Using the Sample Code

Create a new console application named AzureLINQXMLSample, then replace the code in Program.cs with the code attached below.

Change the “accountN” and “secretN” references to your Azure credentials. I decided to embed the storage account information in the code vs. reading it from a config file. This implementation uses a helper class to allow you to specify the account in the constructor, which makes it easy to use different accounts simultaneously. This has worked out well in development. For production, we're storing the data in the Azure config files, which is read from the DAL (data access layer).

Note that you do not have to install the Azure SDK to use the sample code. A vanilla install of .NET 3.5 will work.

Program.cs

Comments