Fun with SqlCommand.ExecuteXmlReader()

SqlCommand_xmlimgSo I’m coding along this morning, minding my own business, when I run up against a really annoying problem with the XML support in the .Net SQL support classes. What I was trying to do was fetch some values from a support table as a simple XML document with an element for each row. The SQLFOR XML AUTOclause was just the ticket, so I wrote the following code:

SqlCommand cmd = new SqlCommand();
cmd.Connection = cn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT * FROM "
  + "supportTable AS valueName FOR XML AUTO";

XmlReader res = cmd.ExecuteXmlReader();

XmlDocument doc = new XmlDocument();

but when I tried to run the code I got the following error on the doc.Load() call:

This document already has a DocumentElement node.

It really had me irritated, because I’d already used the exact same code in another part of my application without a hitch. So after some research, it turns out that the problem (which is obvious in retrospect) is that if multiple rows are returned, the resulting XML is not a valid document, but a document fragment. Since there is no top-level element, the Load() method chokes when it encounters the second row. Not good.

I found several workarounds on the web, but none that I really cared for. So I resorted to some MS SQL funny business that I’ve used in the past that did the trick. The problem here is that there is no single top-level element to serve as a root element. One possible approach is to abandon theFOR XML AUTOin favor ofFOR XML EXPLICIT, but the explicit support is so incredibly complex and unusable that I’d rather cut my own foot off with a rusty tin can lid. So, to save my foot, I resorted to some fun with joins.

TheFOR XML AUTOclause will actually create nested elements in the case of joins between tables, so to create a single top-level element for my support table document I created a new table in my DB calleddummy. It has a single column (dummy) and a single row with a single value (dummy). The single row part is important, because by changing the SQL statement above slightly, I end up with the nice, valid XML document I want:

SELECT * FROM dummy AS rootElementName,
  supportTable AS valueName FOR XML AUTO

Voila! Now my code works and I can move on to more interesting pursuits, like the development of a Comet service for the masses. Good stuff!

Leave a Reply

Your email address will not be published. Required fields are marked *