Thursday, April 14, 2011

How do I optimize a Linq to Xml query againist attributes?

Given the following Xml fragment:

<root>
  <sheetData>
    <row r="1" />
    <row r="2" />
    <row r="3" />
    <row r="4" />
    <row r="5" />
    <row r="6" />
    <row r="7" />
  </sheetData>
</root>

Which can be created with the following code:

XElement testElement = new XElement("root",
    new XElement("sheetData",
        new XElement("row",
            new XAttribute("r", 1)),
        new XElement("row",
            new XAttribute("r", 2)),
        new XElement("row",
            new XAttribute("r", 3)),
        new XElement("row",
            new XAttribute("r", 4)),
        new XElement("row",
            new XAttribute("r", 5)),
        new XElement("row",
            new XAttribute("r", 6)),
        new XElement("row",
            new XAttribute("r", 7))));

Is this the best way to find the row where the r attribute is 2? This works, but I am repeating the Where clause in the select statement, and I am wondering if there is a better way and more efficent method.

int rowNumber = 2;

XElement rowElement = testElement
    .Descendants("sheetData")
    .Where<XElement>(item => item.Descendants("row")
                                  .Where<XElement>(i => i.Attribute("r").Value == rowNumber.ToString())
                                  .FirstOrDefault<XElement>() != null)
    .Select<XElement, XElement>(item => item.Descendants("row")
                                  .Where<XElement>(i => i.Attribute("r").Value == rowNumber.ToString())
                                  .FirstOrDefault<XElement>())
    .FirstOrDefault<XElement>();

In general what is the best way to determine if Linq to Xml query optimized?

From stackoverflow
  • //if sheetData appears multiple times
        XElement rowElement = testElement
            .Descendants("sheetData")
            .SelectMany(s=>s.Descendats("row))
            .Where(i=>i.Attribute("r").Value == rowNumber.ToString());
    //if sheetData appears once
        XElement rowElement = testElement
            .Element("sheetData")
            .Descendants("row))
            .Where(i=>i.Attribute("r").Value == rowNumber.ToString());
    
  • The best way is:

    var row = testElement
       .XPathSelectElements("sheetData/row[@r='2']")
       .FirstOrDefault();
    

    A pure LINQ query that doesn't repeat the Where call:

    var row = testElement
        .Descendants("sheetData")
        .Descendants("row")
        .Where(x => x.Attribute("r").Value == "2")
        .FirstOrDefault();
    
    eglasius : where is the XPathSelectElements defined? if it is an extension method please post the namespace, I would love to have that in XElement :)
    Mehrdad Afshari : I'd use .Where(x => (string)x.Attribute("r") == "2") instead. If no `r` attribute exists, it won't throw NullReferenceException.
    eglasius : found it: http://msdn.microsoft.com/en-us/library/bb342176.aspx ... +1 very nice :) - I will surely use this one quite a bit
  • Isn't the Select clause in your code redundant? Where returns an IEnumerable<XElement> already. I think

    var row = testElements.Descendents("row").Where(e => (int)e.Attribute("r") == rowNumber).SingleOrDefault();
    

    should do what you want

0 comments:

Post a Comment