Despite having spent a large part of the past 12 years writing and optimising SQL queries on various flavours of SQL Server (or perhaps because of that) I still find it difficult to ‘think in Linq’. I see examples of things you can do and all I can think is ‘How do you even start to express such a query?’. I must be getting old.
Case in point – an apparently simple query to take a collection of objects and generate an XML representation. Most of this is easy, but there’s one wrinkle – one of the class members can be null and if it is, I don’t want anything showing up in the destination XML. Such a thing is easy to do in boring old procedural C# with ifs and foreachs, but in Linq it’s not immediately obvious how to express it.
It felt like it might need a subquery but you can’t run a linq query over a simple class – it’s not an enumerable type so linq won’t work. Here’s what I tried:
var xml = new XElement("Points", from poi in walkData select new XElement("PointOfInterest", new XElement("GridReference", new XElement("Easting", poi.gridReference.Easting), new XElement("Northing", poi.gridReference.Northing)), from tween in poi.TweenData where tween != null select new XElement("TweenData", new XElement("Angle",poi.TweenData.Angle), new XElement("TweenType",poi.TweenData.Tween), new XElement("Zoom",poi.TweenData.Zoom))));
But I get the following error:
“Could not find an implementation of the query pattern for source type ‘TweenData’. ‘Where’ not found.”
This is telling me that my TweenData class isn’t enumerable.
So I tried a different tack – what I basically want to do is output something if TweenData isn’t null, so how do I do an if in an expression. I settled on an idiom I generally avoid – the ternary conditional operator – which allows me to either output the XML for a non-null TweenData or pass null. Handily, the XElement constructor is happy to accept null as one of its parameters and output nothing. Here’s the working query:
var xml = new XElement("Points", from poi in walkData select new XElement("PointOfInterest", new XElement("GridReference", new XElement("Easting", poi.gridReference.Easting), new XElement("Northing", poi.gridReference.Northing)), poi.TweenData == null ? null : new XElement("TweenData", new XElement("Angle", poi.TweenData.Angle), new XElement("TweenType", poi.TweenData.Tween), new XElement("Zoom", poi.TweenData.Zoom))));
This query does exactly what I want – if TweenData is non-null it outputs the data in a <TweenData> element, otherwise nothing appears at that point in the output xml.
I still feel uncomfortable about using the query operator. The C++ and C# style guides my team uses explicitly discourages its use, because it often leads to harder to read code. But just now, I can’t think of a more Linqy way to do the same thing. So I’m gritting my teeth and going with what works.