Fun with SqlCommand.ExecuteXmlReader()
So 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 SQL FOR XML AUTO clause 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();
doc.Load(res);
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 the FOR XML AUTO in favor of FOR 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.
The FOR XML AUTO clause 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 called dummy. 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!
WTF, Steve.
So last night I’m trying to add a number on my iPhone, and all of my contacts disappear! I called the helpful Apple support folks, and they had me resync with my PC and my last backup was restored to the phone. Very disconcerting. I probably lost 20 contacts in the deal.
Push it real good.
One of the big limitations of web-based programming is the pull nature of the HTTP protocol. Services like Google Talk, etc. get around this by using a concept that’s now called Comet, which allows the server to “push” content to the client, rather than forcing the client to continually ask for updates.
Since I tend to need this functionality more and more in my Ajax-style apps, I think there should be a web service to make this easier. I’ll keep you posted on my progress.
