<p> Как вы обрабатываете свои XML-данные? С помощью LINQ to XML, XPath, XmlDocument или же сами парсите (тогда вам пора в больничку). Лично я предпочитаю писать XPath-выражение, потому что они (относительно) просты в написании и, соответственно, в чтении. Да и скорость обработки этих выражений всегда радовала своей производительностью. Какое-то время назад вышел extension для LINQ to XML, позволяющий использовать XPath выражения. </p> <p> Небольшой эксперимент позволил определить плюсы и минусы нового расширения по сравнению с LINQ to XML. Для начала я спер базу <a rel="nofollow" href="http://www.codeplex.com/MSFTDBProdSamples" target="_blank">AdventureWorks</a> и экспортировал данные из таблицы SalesOrderDetail в xml файлик. 120000 строк мне показались достаточным набором данных для подобного теста. </p> <p> Затем я написал три различных запроса к этому набору данных с использованием LINQ to XML и тожесамое + XPath. Код и результаты тестов прилагаются. </p> <p> <strong>LINQ to XML: 246.96 мс</strong> </p> <div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; font-size: 8pt; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <span style="color: #008000">/// <summary></span> <span style="color: #008000">/// Выбираем все элементы для ордера с ID=46348</span> <span style="color: #008000">/// </summary></span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">int</span> RunQuery1(XDocument xDoc) { IEnumerable<XElement> elements = from result <span style="color: #0000ff">in</span> xDoc.Descendants(<span style="color: #006080">"dataroot"</span>).Descendants(<span style="color: #006080">"SalesOrderDetail"</span>) <span style="color: #0000ff">where</span> result.Element(<span style="color: #006080">"SalesOrderID"</span>).Value == <span style="color: #006080">"46348"</span> select result; <span style="color: #0000ff">return</span> elements.Count(); } </pre> </div> <p>   </p> <p> <strong>LINQ to XML с XPath: 1025.95 мс</strong> </p> <div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; font-size: 8pt; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">int</span> RunQuery1(XDocument xDoc) { <span style="color: #0000ff">return</span> xDoc.XPathSelectElements(<span style="color: #006080">"//dataroot/SalesOrderDetail[SalesOrderID=46348]"</span>).Count(); } </pre> </div> <p> Правда код с использованием XPath выглядит элегантнее? ОК, идем дальше. </p> <p> <strong>LINQ to XML: 208.88 мс</strong> </p> <div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; font-size: 8pt; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <span style="color: #008000">/// <summary></span> <span style="color: #008000">/// Считаем все SalesOrderDetailы частота заказов > 2 для ProductID=761</span> <span style="color: #008000">/// </summary></span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">int</span> RunQuery2(XDocument xDoc) { IEnumerable<XElement> elements = from result <span style="color: #0000ff">in</span> xDoc.Descendants(<span style="color: #006080">"dataroot"</span>).Descendants(<span style="color: #006080">"SalesOrderDetail"</span>) <span style="color: #0000ff">where</span> String.CompareOrdinal(result.Element(<span style="color: #006080">"OrderQty"</span>).Value, <span style="color: #006080">"2"</span>) > 0 && result.Element(<span style="color: #006080">"ProductID"</span>).Value == <span style="color: #006080">"761"</span> select result; <span style="color: #0000ff">return</span> elements.Count(); } </pre> </div> <p>   </p> <p> <strong>LINQ to XML сXPath: 1013.19 мс</strong> </p> <div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; font-size: 8pt; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">int</span> RunQuery2(XDocument xDoc) { <span style="color: #0000ff">return</span> xDoc.XPathSelectElements(<span style="color: #006080">"//dataroot/SalesOrderDetail[OrderQty>2 and ProductID=761]"</span>).Count(); } </pre> </div> <p>   </p> <p> <strong>LINQ to XML: 218.37 мс</strong> </p> <div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; font-size: 8pt; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <span style="color: #008000">/// <summary></span> <span style="color: #008000">/// Считаем все SalesOrderDetailы, где LineTotal > $4000</span> <span style="color: #008000">/// </summary></span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">int</span> RunQuery3(XDocument xDoc) { IEnumerable<XElement> elements = from result <span style="color: #0000ff">in</span> xDoc.Descendants(<span style="color: #006080">"dataroot"</span>).Descendants(<span style="color: #006080">"SalesOrderDetail"</span>) <span style="color: #0000ff">where</span> String.CompareOrdinal(result.Element(<span style="color: #006080">"LineTotal"</span>).Value, <span style="color: #006080">"4000.00"</span>) > 0 select result; <span style="color: #0000ff">return</span> elements.Count(); } </pre> </div> <p> <strong>LINQ to XML с XPath: 944.9 мс</strong> </p> <div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; font-size: 8pt; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <pre style="border-style: none; margin: 0em; padding: 0px; overflow: visible; font-size: 8pt; width: 100%; color: black; line-height: 12pt; font-family: consolas,'Courier New',courier,monospace; background-color: #f4f4f4"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">int</span> RunQuery3(XDocument xDoc) { <span style="color: #0000ff">return</span> xDoc.XPathSelectElements(<span style="color: #006080">"//dataroot/SalesOrderDetail[LineTotal>4000.00]"</span>).Count(); } </pre> </div> <p> Для наглядности нанесем эти данные на график </p> <p> <a href="/image.axd?picture=WindowsLiveWriter/iLINQtoXMLLINQtoXMLXPath_9946/image_2.png"><img style="border: 0px none " src="/image.axd?picture=WindowsLiveWriter/iLINQtoXMLLINQtoXMLXPath_9946/image_thumb.png" border="0" alt="image" title="image" width="487" height="295" /></a> </p> <p> Теперь всем видно, что LINQ to XML куда быстрее, чем тоже самое но с использованием XPath. Зачем же Майкрософт написало такую надстройку? </p> <p> XPath удобнее использовать, когда </p> <ul> <li>код должен быть читабельным</li> <li>при миграции продуктов, использующих XPath исторически</li> <li>когда приложение не требует большой производительности от доступа к данным. Мой набор данных имел 120000 строк и при этом XPath работал на 1 секунду медленнее, чем LINQ to XML. Не так уж и плохо, верно?</li> </ul> <p> Но лучше от XPath отказаться, если </p> <ul> <li>производительность делает вам погоду</li> <li>вам необходим sql-похожий синтаксис (ну мало ли, может вы sql-маньяк)</li> <li>вам необходима группировка (к примеру, посчитать сумму ордера)</li> <li>вам небоходим функционал агрегирования (суммы, среднее и прочее)</li> </ul> <p> Вообщем на те вам, и выбирайте что хотите. </p>

Как вы обрабатываете свои XML-данные? С помощью LINQ to XML, XPath, XmlDocument или же сами парсите (тогда вам пора в больничку). Лично я предпочитаю писать XPath-выражение, потому что они (относительно) просты в написании и, соответственно, в чтении. Да и скорость обработки этих выражений всегда радовала своей производительностью. Какое-то время назад вышел extension для LINQ to XML, позволяющий использовать XPath выражения.

Небольшой эксперимент позволил определить плюсы и минусы нового расширения по сравнению с LINQ to XML. Для начала я спер базу AdventureWorks и экспортировал данные из таблицы SalesOrderDetail в xml файлик. 120000 строк мне показались достаточным набором данных для подобного теста.

Затем я написал три различных запроса к этому набору данных с использованием LINQ to XML и тожесамое + XPath. Код и результаты тестов прилагаются.

LINQ to XML: 246.96 мс

/// <summary>
/// Выбираем все элементы для ордера с ID=46348
/// </summary>
public static int RunQuery1(XDocument xDoc)
{
IEnumerable<XElement> elements =
from result in xDoc.Descendants("dataroot").Descendants("SalesOrderDetail")
where result.Element("SalesOrderID").Value == "46348"
select result;
return elements.Count();
}

 

LINQ to XML с XPath: 1025.95 мс

public static int RunQuery1(XDocument xDoc)
{
return xDoc.XPathSelectElements("//dataroot/SalesOrderDetail[SalesOrderID=46348]").Count();
}

Правда код с использованием XPath выглядит элегантнее? ОК, идем дальше.

LINQ to XML: 208.88 мс

/// <summary>
/// Считаем все SalesOrderDetailы частота заказов > 2 для ProductID=761
/// </summary>
public static int RunQuery2(XDocument xDoc)
{
IEnumerable<XElement> elements =
from result in xDoc.Descendants("dataroot").Descendants("SalesOrderDetail")
where String.CompareOrdinal(result.Element("OrderQty").Value, "2") > 0
&& result.Element("ProductID").Value == "761"
select result;
return elements.Count();
}

 

LINQ to XML сXPath: 1013.19 мс

public static int RunQuery2(XDocument xDoc)
{
return xDoc.XPathSelectElements("//dataroot/SalesOrderDetail[OrderQty>2 and ProductID=761]").Count();
} 

 

LINQ to XML: 218.37 мс

/// <summary>
/// Считаем все SalesOrderDetailы, где LineTotal > $4000
/// </summary>
public static int RunQuery3(XDocument xDoc)
{
IEnumerable<XElement> elements =
from result in xDoc.Descendants("dataroot").Descendants("SalesOrderDetail")
where String.CompareOrdinal(result.Element("LineTotal").Value, "4000.00") > 0
select result;
return elements.Count();
}

LINQ to XML с XPath: 944.9 мс

public static int RunQuery3(XDocument xDoc)
{
return xDoc.XPathSelectElements("//dataroot/SalesOrderDetail[LineTotal>4000.00]").Count();
}

Для наглядности нанесем эти данные на график

image

Теперь всем видно, что LINQ to XML куда быстрее, чем тоже самое но с использованием XPath. Зачем же Майкрософт написало такую надстройку?

XPath удобнее использовать, когда

  • код должен быть читабельным
  • при миграции продуктов, использующих XPath исторически
  • когда приложение не требует большой производительности от доступа к данным. Мой набор данных имел 120000 строк и при этом XPath работал на 1 секунду медленнее, чем LINQ to XML. Не так уж и плохо, верно?

Но лучше от XPath отказаться, если

  • производительность делает вам погоду
  • вам необходим sql-похожий синтаксис (ну мало ли, может вы sql-маньяк)
  • вам необходима группировка (к примеру, посчитать сумму ордера)
  • вам небоходим функционал агрегирования (суммы, среднее и прочее)

Вообщем на те вам, и выбирайте что хотите.

blog comments powered by Disqus