Ввместе с «новыми» возможностями, которые дает .NET Framework 3.5, работа с LINQ превращается в сплошное удовольствие. В настоящее время Microsoft представила несколько провайдеров LINQ (связующих звеньев с определенными сущностями, такими как XML, DATASET или простой объект): LINQToOBJECTS, LINQToSQL, LINQToDATASET, LINQToENTITIES и LINQToXML. Последний из перечисленных мы и обсудим в данной статье.
Подробно рассматривать основы LINQToXML не имеет смысла — это тема не просто отдельной статьи, а как минимум большого раздела в книге. Здесь вам будут предложены лишь отдельные приемы использования LINQToXMl, которые помогут понять всю мощь данного языка запросов. Для примера разберем работу обычного оконного Windows-приложения, исполняющего несколько запросов LINQ, в частности создание XML-документа, поиск в нем, редактирование элемента, получение некоторых данных, сортировка и трансформация в другой документ XML. Описание работы с интерфейсом программы опустим, так как те, кто знает основы C#, сумеют реализовать эту часть самостоятельно. Тем не менее на иллюстрациях показана главная форма выбранной для примера программы. Впрочем, она может быть совершенно другой, все зависит от того, что вам удобнее. Итак, приступим.
Работать с XML в C#-приложении можно было и раньше, причем разными способами. Объединяло их одно: код получался либо слишком громоздким, либо «опасным», так как в него вполне могли закрасться мелкие ошибки (если вы работали с документом как с простой строкой, т. е. редактировали обычный текстовый файл в виде строкового типа данных, а затем сохраняли его с расширением .xml). Технология LINQToXML решает обе эти проблемы. Для примера создадим простой документ (листинг 1).
В этом классе будут еще некоторые методы. Пока же обратите внимание на переменную elem, содержащую наш документ (переменная могла бы быть и типа XDocument — для рассматриваемого примера это неважно). В методе GenerateItems мы генерируем документ. Обратите внимание, что уже в процессе генерации можно представить его структуру, так как она видна прямо из текста программы. Раньше об этом приходилось только мечтать. Сам документ представляет собой отрывок из упрощенного документа книжного интернет-магазина. Он содержит такие данные, как описание книг (название, автор и издательство) и количество экземпляров на складе. Для начала давайте получим все названия книг из нашего документа (листинг 2)
В данном методе имеется переменная titles, где будут содержаться строки с названиями книг. В цикле foreach мы с помощью elem.Descendants () вызываем все дочерние элементы по отношению к элементу books (а это будут, естественно, все элементы book), в которых запрашиваем элементы title, содержащие названия, а потом добавляем их в наш List. Согласитесь, что это всего пара строк кода. В былые годы не удавалось так удобно работать с XML.
Если известно название книги, то можно получить все остальные данные по ней следующим способом (листинг 3).
Данный пример в целом похож на предыдущий, только вместо Descendants был использован Elements с указанием в качестве параметра необходимых элементов. Доступ к значениям элементов открывает свойство Value элемента. Если не применять его, то будет получен весь элемент. Возвращаемое значение — класс Book, являющийся вспомогательным для рассматриваемых примеров (листинг 4).
Теперь давайте вернем названия книг, отсортированные по количеству экземпляров на складе. Причем сделаем это так, чтобы самые популярные на складе книги шли первыми. Раньше нужно было потрудиться, чтобы получить такой результат, а теперь достаточно всего пяти строк (листинг 5).
Сейчас следует опять обратиться к элементам book, отсортировав их по значению атрибута items, а затем просто выбрать из этого элемента названия. Здесь важно отметить, что нам пришлось создать простенький класс BookComparer, чтобы сортировать книги не по принципу «наиболее редкие в начале», а в необходимом нам порядке, когда наиболее популярные книги идут впереди. Сделать это можно было бы и без вспомогательного класса, просто вызвав несколько иную вариацию OrderBy, но в данном случае имеет смысл поступить именно так, ведь в более сложных примерах могут понадобиться вспомогательные классы. И лучше знать о том, как их применять. Вот примененный нами вспомогательный класс (листинг 6).
Если что-то здесь непонятно, то изучите интерфейс iComparer в документации Microsoft MSDN. Подробный разбор данного интерфейса выходит за рамки этой статьи. Давайте теперь реализуем поиск по всем элементам следующим образом (листинг 7).
Новым для вас здесь является метод Where. В нем, собственно, проверяется, содержит ли какой-либо из элементов искомое значение. Если оно там находится, то мы выбираем его родителя, т. е. сам элемент book. После чего вызываем метод Distinct. Это нужно сделать по той простой причине, что в случае, если искомое содержится в поле автора и, скажем, издательства, то элемент, включающий искомое, будет добавлен дважды. Distinct удаляет дубликаты. Далее просто получаем название книги, в данных которой есть искомое значение.
Обновить элемент можно разными способами. Разберем один из них (листинг 8).
По сути, мы находим требующуюся книгу по ее названию, содержащемуся в переменной old_title. Обнаружив необходимый элемент, мы вызываем First (предполагая, что одинаковых книг нет в документе), берем его родителя (элемент book) и просто заменяем новыми данными.
Теперь давайте возьмемся за что-нибудь посложнее, например за трансформацию документа. Попробуем сделать из нашего документа следующий (листинг 9).
Для этого напишем метод (листинг 10).
В самой первой строке мы группируем данные по издательству. Для этого обращаемся к значению элемента Publishing и делаем его ключом группировки. А данные, которые будем группировать, — простые элементы book. Потом создаем сам документ, причем сначала его корневой элемент, а затем, в цикле foreach, и все остальные, просто пробегаясь по нашим группам и делая в процессе этого элементы. Не правда ли, изящно?
В конце мы просто сохраняем трансформированный xml-файл. Кстати, раз уж речь зашла о сохранении и загрузке файлов, то такие операции выполняются достаточно просто. Сохранение уже было представлено выше, а загрузка реализуется следующим образом:
public void Load (string name)
{
elem = XElement.Load (name);
}
А так можно удалить все данные, хранящиеся в XElement:
public void Clear ()
{
elem.RemoveAll ();
}
Конечно, в статье были рассмотрены далеко не все возможности LINQToXML, просто вас познакомили с некоторыми приемами работы с ним. Вполне вероятно, что какие-то из них помогут лично вам. И еще надеюсь, что те, кто пока не приступил к изучению данного интегрированного языка запросов, после того, как прочитают этот материал, пожелают познакомиться с LINQToXML. В конце концов, Microsoft вложила много сил в LINQ, и данная технология действительно способна повысить эффективность работы.