лекция. Зиборов. Справочник для опытных и как пособие для начинающих программистов. Компактдиск содержит исходные коды примеров из книги
Скачать 7.39 Mb.
|
Листинг 11.3. Группировка элементов списка с помощью LINQ-запросов //Программа формирует список некоторых продуктов питания. Первый LINQ-запрос // группирует элементы списка по критерию цены: в первом списке оказываются // продукты, цена за единицу которых меньше или равна 90 руб., а во втором, // соответственно, больше 90 руб. Второй LINQ-запрос вычисляет среднюю цену // продукта по каждой группе. Результаты запросов выводятся в текстовое поле using System; using System.Collections .Generic; using System.Linq; using System.Windows.Forms; //Другие директивы using удалены, поскольку они не используются в данной программе namespace LinqЦеныНаПродукты { public partial class Form1 : Form public Form1() { InitializeComponent(); textBox1 .Multiline = true; base.Text = "Группировка элементов списка с помощью LINQ-запроса"; // Заполняем список продуктов: List<Пpoдукт> Продукты = new List<Пpoдукт>{ new Продукт {Наименование="Творог", Цена=112.50F }, new Продукт {Наименование="Хлеб", Цена=18.75F }, new Продукт {Наименование="Печенье", Цена=93.75F }, new Продукт {Наименование="Чай", Цена=76.25F }, new Продукт {Наименование="Мясо", Цена=150.00F }, new Продукт {Наименование="Гречка", Цена=62.50F },, }; var Запрос1 = from П in Продукты group П by new { Критерий = П.Цена > 90, } into g select g; var Запрос2 = from p in Продукты group p by p.Цена > 90 into g select new { g.Key, СредЦенаПоГруппе = g.Average(p => p.Цена) }; Single СредЦенаПоГруппе1 = Запрос2.ElementAt(0).СредЦенаПоГруппе; Single СредЦенаПоГруппе2 = Запрос2.ElementAt(1).СредЦенаПоГруппе; // Вывод результатов обоих запросов в текстовое поле: foreach (var Группа in 3anpoc1) { if (Группа.Key.Критерий .== false) textBox1.Text += "\r\nЦены 90 руб или меньше:"; else textBox1.Text += "\r\nЦены больше 90 руб:"; foreach (var Прод in Группа) { textBox1.Text += String.Format("\r\n{0} - {1}", Прод.Наименование, Прод.Цена); } if (Группа.Key.Критерий == false) textBox1.Text += String.Format( "\r\nСредняя цена по данной группе = {0} руб.\r\n", СредЦенаПоГруппе2); else textBox1.Text += String.Format( "\r\nСредняя цена по данной группе = {0} руб.\г\п", СредЦенаПоГруппе1); } } } public class Продукт { public String Наименование { get; set; } public Single Цена { get; set; } } ) В начале программы формируем список продуктов питания. Для этого объявляем новый класс Продукт, в котором для каждого продукта задаем два поля: наименование продукта и его цену. После заполнения списка продуктов (для упрощения в этом списке всего шесть продуктов) задаем первый LINQ-запрос, обеспечивающий деление списка на две группы. Второй LINQ-запрос вычисляет среднюю цену продукта по каждой группе. Вывод результатов обоих запросов организуем с помощью двух вложенных циклов foreach. Фрагмент работы программы показан на рис. 11.3. Рис. 11.3. Группировка списка с помощью LINQ-запросов Убедиться в работоспособности программы можно, открыв решение LinqЦеныНаПродуктыю.sln папки LinqЦеныНаПродукты. Пример 78. LINQ-запрос к словарю данных Dictionary Замечательной структурой данных является словарь Dictionary. Он представит собой совокупность (коллекцию) ключей и значений. То есть каждый элемент (запись), добавляемый в словарь, состоит из значения Value и связанного с ним ключа Key. Извлечение значения по его ключу происходит очень быстро, поскольку класс Dictionary<Key, Value> реализован как хэш-таблица. Каждый ключ словаре Dictionary<Key, Value> должен быть уникальным, т. е. единственным своем роде, эксклюзивным. При добавлении в коллекцию Dictionary очередного элемента так называемый компаратор проверяет на равенство уникальность нового ключа. Ключ не может быть пустым (null), а значение может, если тип значения Value является ссылочным типом. Возможно создание словаря, в котором не различается регистр символов. Использование словаря Dictionary может существенно повлиять на эффективность алгоритма, на простоту его понимания и легкость программной реализации. Задача, решаемая в данном примере, состоит в том, чтобы продемонстрирова возможность выбора элементов из словаря данных Dictionary с помощью LINQ- запроса. В начале программы зададим массив сотрудников некого учреждения, затем массив сотрудников преобразуем в словарь Dictionary. Причем в качестве ключа к каждому элементу словаря зададим имя сотрудника, которое является уникальным. По ключу можно получить все остальные сведения о сотруднике, записанные в словарь. Кроме того, с помощью LINQ-запроса к словарю можно получать новую коллекцию сотрудников по какому-либо условию (предложение where). Здесь мы зададим выбор сотрудников, чей возраст превышает 33 года (возраст Иисуса Христа). Для решения этой задачи запустим Visual Studio 2010 и выберем проект шаблона Console Application, укажем имя Name — LinqDictionary. Затем на вкладке программного кода введем текст, представленный в листинге 11.4. Листинг 11.4. Организация LINQ-запроса к словарю Dictionary // Задаем массив сотрудников учреждения. Из этого массива создаем словарь // сотрудников, а в качестве ключа к этому словарю выбираем имя сотрудника. //С помощью LINQ-запроса из массива сотрудников выбираем тех, чей возраст // превышает 33 года. При выводе результата запроса на печать учитываем, что //мы говорим "47 лет", но "34 года". То есть если из возраста // вычесть число, кратное 10, то при остатке меньше 5 говорят, // например, "34 года", а при остатке больше или равном 5 говорят "47 лет" using System; using System.Linq; // Другие директивы using удалены, поскольку они не используются в данной программе namespace LinqDictionary { class Program { static void Main(string[] args) { Console.Title = "LINQ-запрос к словарю Dictionary"; // Создаем массив сотрудников учреждения: var Сотрудники = new[] { new {Имя = "Карапузова Ирина",» Возраст = 27, КуритЛи = true }, new {Имя = "Зиборов Виктор", Возраст = 47, КуритЛи = false }, new {Имя = "Ломачинская Светлана", Возраст = 31, КуритЛи = false }, new {Имя = "Стороженко Светлана", Возраст = 34, КуритЛи = false }, new {Имя = "Еременко Татьяна", Возраст = 22, КуритЛи = true }, new {Имя = "Погребицкий Олег", Возраст = 42, КуритЛи = true } }; // Доступ к элементу массива Сотрудники можем иметь через его индекс: var t = Сотрудники [2] ; //Строим LINQ-запрос к массиву сотрудников; выбираем тех, // чей возраст больше 33 лет: // var СписокВзрослыхСотрудников = from Сотрудник in Сотрудники // where Сотрудник.Возраст >= 33 // orderby Сотрудник.Имя // select Сотрудник; //Из массива сотрудников создаем словарь сотрудников, в котором // ключом является имя сотрудника: var СловарьСотрудников = Сотрудники.ToDictionary(Ключ => Ключ.Имя); // В этом случае очень удобным становится доступ к сведениям //о сотруднике по его имени: Boolean КуритЛиЗиборов = СловарьСотрудников ["Зиборов Виктор"].КуритЛи; int ВозрастЗиборова = СловарьСотрудников["Зиборов Виктор"].Возраст; Console.WriteLine("Сотруднику Зиборову Виктору - {0} лет\n", ВозрастЗиборова); // Строим LINQ-запрос к словарю сотрудников; выбираем тех, // чей возраст больше 33 лет: var СписокВзрослыхСотрудников = from Сотрудник in СловарьСотрудников where Сотрудник.Value.Возраст >= 33 orderby Сотрудник.Value.Имя select new { Сотрудник.Key, Сотрудник.Value.Возраст, ЛетИлиГода = Сотрудник.Value.Возраст - 10F * Math.Truncate(Сотрудник.Value.Возраст / 10F) >= 5 }; // Вывод результата запроса на консоль: Console.WriteLine("Список сотрудников, старше 33 лет:"); foreach (var x in СписокВзрослыхСотрудников) Console.WriteLine("{0}, возраст - {1} {2} ", x.Key, x.Возраст, х.ЛетИлиГода ? "лет" : "года"); Console.ReadKey(); } } } Представленный текст программы очевиден. Вначале задаем массив сотрудников учреждения, в этом массиве всего 6 элементов. В комментарии показано, как можно организовать LINQ-запрос к заданному массиву. Далее массив конвертируем в словарь сотрудников, задавая в качестве ключа имя сотрудника. Затем организуем LINQ-запрос к словарю Dictionary, выбираем в новую коллекцию (список тех, чей возраст превышает 33 года. Учитываем, что при выводе на консоль списка выбранных сотрудников мы говорим "47 лет", но "34 года". То есть если из возрас. та вычесть число, кратное 10, то при остатке меньше 5 говорят, например, "34 года", а при остатке больше или равном 5 говорят "47 лет". Число, кратное 10, мы вычисляем, используя функцию Math.Truncate, которая возвращает целую часть числа, поданную на ее вход. В этом алгоритме мы использовали булеву переменную ЛетИлиГода, которая определяет (по принципу "да" или "нет") следует писать "лет" или "года". Результат работы программы показан на рис. 11.4. Рис. 11.4. Запрос к словарю данных на "взрослых" сотрудников Убедиться в работоспособности программы можно, открыв решение LinqDictionary.sln папки LinqDictionary. Пример 79. Создание XML-документа методами классов пространства имен System.Xml.Linq Кроме пространства имен System.Xml, содержащего классы для обработк XML-документов, в Visual С#2010 имеем пространство имен System.Xml.Linq, содержащее классы, которые позволяют легко и эффективно изменять документы XML, а также организовывать LINQ-запросы. В данном примере оформим сведения о наших повседневных телефонных контактах в XML-документ. Этот документ будет иметь интуитивно понятную структуру: имя контакта, домашний и мольный телефоны. Создав такой XML-документ и получив соответствующий XML -файл, его очень удобно просмотреть в MS Excel в виде таблицы, содержащей три столбца: имя контакта, домашний телефон и мобильный. Попутно обсудим структуру XML-документа. Итак, запустим среду Visual Studio 2010, выберем проект шаблона Console Арplication, укажем имя Name — LinqСоздатьXML-документ. Затем на вкладке программного кода введем текст, представленный в листинге 11.5. Листинг 11.5. Создание XML-документа представляющего телефонную книгу //Программа создает типичный XML-документ. С ее помощью можно разобраться в // структуре XML-документа. В комментариях приведена терминология содержимого //XML-документа: корневой элемент, вложенные элементы, имя элемента и его //значение, а также атрибуты элемента, их имена и значения. XML-документ //представляет телефонную книгу, содержащую имя контакта, номер домашнего //телефона, а также мобильного. Программа после создания XML-документа // отображает его на консоли, а также записывает его в файл. Если этот файл // открыть с помощью MS Excel, то мы получим таблицу из трех столбцов, using System; using System.Xml.Linq; // Другие директивы using удалены, поскольку они не используются в данной программе namespace LinqСоздатьXML_документ { class Program { static void Main(string[] args) { Console.Title = "Корневой элемент XML-документа"; // Создаем новый XML-документ: XDocument XMLдокумент = new XDocument( // Комментарий в XML-документе: new XComment("Телефонная_книга - это корневой элемент XML-документа:"), new XElement("Телефонная_книга", // - имя корневого элемента new XComment("Элемент СТРОКА содержит атрибут Контакт и два вложенных элемента"), new XElement("СТРОКА", // - имя (Name) элемента new XAttribute("Контакт", "Олег"), new XElement("Домашний_телефон", "236-23-67"), // _ имя элемента и его значение new XElement("Мобильный_телефон", "+7(495)625-31-43")), new XComment("Атрибут Контакт имеет значение 'Прогноз погоды':"), new XElement("СТРОКА", new XAttribute("Контакт", "Прогноз погоды"), // - атрибут элемента СТРОКА new XElement("Домашний_телефон", "001"), new XElement("Мобильный_телефон", "")), // - имя элемента и его зачение (Value) new XComment("Поскольку каждый элемент Контакт имеет атрибут и два вложенных=>"), new XElement("СТРОКА", new XAttribute("Контакт", "Борис Григорьевич"), // - имя атрибута - Контакт new XElement("Домашний_телефон", "402-12-45"), new XElement("Мобильный_телефон", "+7(495)536-79-94")), new XComment("=> элемента, в MS Excel отобразится таблица с тремя колонками"), new XElement("СТРОКА", new XAttribute("Контакт", "Света"), // - значение атрибута - Света new XElement("Домашний_телефон", ""), new XElement("Мобильный_телефон", "+7(495)615-24-41"))) ) ; // Сохранить XML-документ: XMLflOKyMeHT.Save(@"С:\Зиборов.XML"); Console.WriteLine(XMLдокумент); Console.ReadKey(); } } } Чтобы понять текст программы, рассмотрим структуру полученного ХМL-файла, а для этого откроем этот файл с помощью Internet Explorer (рис. 11.5). Здесь весь XML-документ вложен в так называемый корневой элемент между начальным тегом <Телефонная_книга> и конечным тегом Телефонная_книга>. Четыре элемента строка вложены в корневой элемент. В соответствующей таблице MS Excel элементы строка будут представлять строку в таблице. В свою очередь элемент строка содержит в себе атрибут Контакт и два вложенных в него элемента, имена (Name) которых — Домашний_телефон и Мобильный_телефон. Именно поэтому в MS Excel отобразится таблица с тремя колонками (один атрибут и два элемента): "Контакт"."Домашний_телефон" и "Мобильный_телефон". Рис. 11.5. XML-файл, открытый в Internet Explorer Элемент может иметь один или несколько атрибутов (а может и не иметь, как, скажем, элемент Домашний_телефон), например, первый элемент строка имеет атрибут с именем (Name) Контакт и со значением атрибута (value) — 001. После запуска данной программы будет выведено на консоль содержимое XML-документа (без XML-объявления), а также будет создан XML-файл. Открыв этот файл с помощью MS Excel, получим таблицу телефонных контактов (рис. 11.6). Рис. 11.6. XML-файп, открытый в MS Excel Убедиться в работоспособности программ, можно, открыв решение LinqСоздатьXML_документ.sln папки LinqСоздатьXML_документ. Пример 80. Извлечение значения элемента из XML-документа В данном примере, решая задачу, мы получили строку XML-данных, наприщ от удаленной Web-службы, обеспечивающей прогнозом погоды. В этой текстоввой XML-строке содержатся метеорологические показатели на заданный нами район, для текущей даты. В данной задаче мы извлекаем из этих XML-данных только значение температуры. Несколько слов о структуре XML- документа ( рис. 11.7). Рис. 11.7. Содержимое XML-файла с метеорологическими показателями Как видно, XML-документ начинается с XML-объявления (XML declaration), в котором содержится информация о версии (version information parameter). А далее весь XML-документ состоит из вложенных друг в друга элементов. Элемент — это блок разметки между начальным тегом, например <Город>, и конечным тегом Город>. Самый внешний элемент, в данном случае— это тег <МетеорологическиеПоказатели>, его называют корневым элементом (root element). Как видите, этот корневой элемент содержит в себе все показатели, и, таким образом, глубина вложенности этой иерархии равна двум. Вообще говоря, глубина вложенности такого XML-дерева может быть практически любой. Итак, задача поставлена, сущность XML-данных понятна, приступаем к решению задачи. Для этой цели после запуска Visual Studio 2010 выберем проект шаблона Windows Forms Application, укажем имя Name — Linq3. Далее, попав в конструктор формы, из панели элементов Toolbox перетащим текстовое поле TextBoх для вывода в него строки с данными XML и значения температуры из соответствующего элемента XML-дерева. Поскольку мы предполагаем вывод в текстовое поле не одной, а нескольких строчек, в свойствах объекта textBox1 укажем truе напротив свойства Multiline. Затем на вкладке программного кода введем текст, представленный в листинге 11.6. Листинг 11.6. Извлечение значения элемента из XML-данных // Дана строка XML, содержащая прогнозные метеорологические показатели // для Москвы на заданную дату. Программа извлекает из корневого элемента // XML-документа значение температуры элемента "Температура" using System.Windows. Forms; // Другие директивы using удалены, поскольку они не используются в данной программе namespace Linq3 { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.Text = "LINQ-запрос к XML-данным"; textBox1.Multiline = true; string СтрокаXML = @" <МетеорологическиеПоказатели> <Город>МоскваГород> <Дата>2010.05.15 06:30 UТСДата> <Температура> 64 F (18 С)Температура> <Ветер>Сев-Вост 8 м/секВетер> <Видимость>12 кмВидимость> <Влажность> 72%Влажность> <Давление>760 мм рт стДавление> МетеорологическиеПоказатели>"; // Загрузка корневого элемента из строки, содержащей XML: var КорневойЭлемент = System.Xml.Linq.XElement.Parse (СтрокаХML); // Или корневой элемент XML-документа получаем через файл: // записываем строку, содержащую XML, в файл // System.IO.File.WriteAllText("ПоказателиПогоды.xml", CтрокаXML); // Загружаем корневой элемент XML: // var КорневойЭлемент = System.Xml.Linq. // XElement.Load("ПоказателиПогоды.xml"); //Из корневого, элемента извлекаем вложенный в него элемент // "Температура" и получаем соответствующее значение (Value) // этого элемента: string Температура = КорневойЭлемент.Element("Температура").Value; textBox1.Text = "Строка XML:\r\n\r\n" + CтрокaXML + "\r\n\r\n"; textBox1.Text += "Значение температуры = " + Температура; } } } Рис. 11.8. Извлечение значения элемента из XML-документа В начале текста программы задаем текстовую строку, содержащую XML- данные. Далее, используя метод Parse класса XElement пространства имен Linq получаем корневой элемент XML-документа. В комментарии показано, как можно получить корневой элемент через запись/чтение XML-файла. Затем с помощью метода Element извлекаем значение (Value) элемента Температура, которое вывод в текстовое поле. Фрагмент работы программы приведен на рис. 11.8.. Рис. 11.9. Представление XML-данных в виде таблицы в MS Excel Убедиться в работоспособности программы можно, открыв решение Linq3.sln папки Linq3. Теперь решим похожую задачу по извлечению значения элемента, но пусть XML-данные представлены в другой форме, а именно каждый метеорологический показатель вложим в один и тот же элемент <показатель> показатель>, в этом случае глубина вложенности элементов будет уже равна трем (см. листинг 11.7). Естественно спросить: что мы будем с этого иметь? Дело в том, что если соответствующий XML-файл открыть с помощью табличного редактора MS Excel, то мы можем увидеть эти XML-данные в виде наглядной таблицы, даже не ссылаясь на таблицу стилей — файл XSLT (не путать с XLS-файлом), см. рис. 11.9. Теперь для получения значения температуры удобно воспользоваться типовым LINQ-запросом (листинг 11.7). Листинг 11.7. Извлечение значения элемента из XML-данных // Дана строка XML, которая содержит прогнозные метеорологические // показатели для Москвы на заданную дату. При этом каждый метеорологический // показатель вложен в один и тот же элемент <Показатель> Показатель>. // Это обеспечивает удобный просмотр соответствующего XML-файла в MS Excel // в виде таблицы. Программа находит в корневом элементе данного XML-документа // элемент "Температура" и извлекает из него значение температуры. using System.Windows . Forms ; using System.Linq; // Другие директивы using удалены, поскольку они не используются в данной программе namespace Linq4 { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.Text = "LINQ-запрос к XML-данным"; textBox1.Multiline = false; // Инициализация XML-строки: string CтрокаXML = @" <МетеорологическиеПоказатели> <Показатель> <Город>МоскваГород> Показатель> <Показатель > <Дата>2010.05.15 06:30 UТСДата> Показатель> <Показатель> <Температура> 64 F (18 С)Температура> Показатель> <Показатель> <Ветер>Сев-Вост 8 м/секВетер> Показатель> <Показатель> <Видимость>12 кмВидимость> Показатель> <Показатель> <Влажность> 72%Влажность> Показатель> <Показатель> <Давление>760 мм рт стДавление> Показатель> МетеорологическиеПоказатели>"; var КорневойЭлемент = System.Xml .Linq.XElement. Parse (СтрокаХМL) ; // Или корневой элемент получаем через файл: // записываем строку, содержащую XML, в файл // System.10.File.WriteAllText("ПоказателиПогоды2.xml", СтрокаХМL); // Загружаем корневой элемент: // var КорневойЭлемент = System.Xml.Linq.XElement.Load( // "ПоказателиПогоды2.xml"); // Запрос - это коллекция (список) строк, в которую извлекаем // значение (Value) элемента "Температура": var Запрос = from х in КорневойЭлемент.Elements("Показатель") from у in х.Elements("Температура") select у.Value; // Таких строк в коллекции Запрос - одна textBox1.Text = "Значение температуры = "; foreach (var х in Запрос) textBox1.Text = textBox1.Text + x; } } } Как видно из программного кода, поиск организован в двух уровнях (два предложения from), сначала выбор в коллекцию всех элементов Показатель, а затем из этой коллекции поиск элементов Tемпература. Результат запроса записывается в коллекцию строк, где имеем ровно одну строку. Фрагмент работы программы показан на рис. 11.10. Рис. 11.10. LINQ-запрос к XML-документу Убедиться в работоспособности программы можно, открыв решение Linq4.sln из папки Linq4. Пример 81. Поиск строк (записей) в XML-данных с помощью LINQ-запроса Имеем XML-данные, в которых содержится традиционная для нашей книги таблица с именами и телефонами, причем имена в этой телефонной табличке повторяются, например, строка с именем "Витя" содержит мобильный телефон, а потом по мере знакомства с этим Витей у нас появился уже и его домашний телефон. Задача состоит в том, чтобы в данной таблице телефонов (представленной в виде XML, см. листинг 11.8) найти все строчки с именем "Витя". Эта маленькая несерьезная, на первый взгляд, задача подразумевает, например, такую уже "серьезную" задачу. Имеем громадную базу данных, которую мы получили на каком-то этапе обработки в виде XML, и нам требуется "отфильтровать" записи в этой базе на предмет содержания в некотором поле определенной строки. Прежде чем решать данную задачу, давайте посмотрим отображение обсуждаемых XML-данных в табличном редакторе MS Excel (рис. 11.11). Рис. 11.11. Отображение XML-данных в MS Excel Как видно, в редакторе MS Excel наши XML-данные представлены весьма наглядно. И очень понятно, что мы хотим получить, а именно, все номера телефонов напротив имени "Витя". Для решения этой задачи запустим Visual Studio 2010 и выберем проект шаблона Windows Forms Application, укажем имя Name — Linq5. Далее, попав в конструктор формы, из панели элементов Toolbox перетащим текстовое поле TextBoх для вывода в него найденных строк из таблицы XML. В свойствах текстового поля разрешим ввод множества строк (а не одной), для этого свойство Multiline переедем в состояние true. Затем на вкладке программного кода введем текст, представленный в листинге 11.8. Листинг 11.8. Извлечение значения элемента из XML-данных //Имеем XML-данные, в которых содержится таблица с именами и телефонами, // причем имена в этой телефонной табличке повторяются. Задача состоит в том, // чтобы в данной таблице телефонов (представленной в виде XML) найти все // строчки с именем "Витя" с помощью LINQ-запроса using System.Linq; using System.Windows.Forms; // Другие директивы using удалены, поскольку они не используются в данной программе namespace Linq5 { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.Text = "LINQ-запрос к XML-данным"; textBox1.Multiline = true; // Инициализация XML-строки: string CтрокaXML = @" <ТаблицаТелефонов> <Строка> <Имена>ВитяИмена> <Номера_телефонов>274 28 44Номера_телефонов> Строка> <Строка> <Имена>АндрейИмена> <Номера_телефонов> 8-085-45б-2378Номера_телефонов> Строка> <Строка> <Имена>Карапузова ТаняИмена> <Номера_телефонов>4 45-56-47Номера_телефонов> <Строка> <Имена>ВитяИмена> <Номера_телефонов>099 72 161 52Номера_телефонов> Строка> <Строка> <Имена>НикипеловИмена> <Номера_телефонов>2 3 6-77-7 6Номера_телефонов> Строка> <Строка> <Имена>ЗиборовИмена> <Номера_телефонов>254 67 97Номера_телефонов> Строка> ТаблицаТелефонов>"; var КорневойЭлемент = System.Xml.Linq.XElement.Parse(CтрoкaXML); // Запись строки, содержащей XML, в, файл: // System.IO.File.WriteAllText("ТаблицаТелефонов.xml", CтрокaXML); // var КорневойЭлемент = System.Xml.Linq. // XElement.Load("ТаблицаТелефонов.xml"); var Записи = from x in КорневойЭлемент.Elements("Строка") where (string)x.Element("Именa") == "Витя" select x.Element ("Номера_телефонов") .Value; textBox1.Text = textBox1.Text + @"Строки, содержащие имя ""Витя"":" + "\r\n"; // Вывод коллекции записей в текстовое поле textBox1: foreach (var х in Записи) textBox1.Text = textBox1.Text + x + "\r\n"; // Таких записей в этой коллекции - ровно одна } } } Как видно, в начале программы мы инициализируем (т.е. присваиваем начальные значения) XML-строку. Далее извлекаем корневой элемент из XML-документа, oн, по сути, отличается от XML-документа отсутствием XML-объявления (в этом можно убедиться в отладчике программы). В комментарии указано, как можно получить корневой элемент в том случае, если он представлен в виде XML-файла во внешней памяти. Затем организуем типовой, стандартный LINQ-запрос. Результат запроса попадает в коллекцию записей, которую выводим в текстовое поле, используя оператор цикла foreach. Фрагмент работы программы показан на рис. 11.12. Убедиться в работоспособности программы можно, открыв решение Linq5.sln из папки Linq5. Рис. 11.12. LINQ-запрос к XML-документу Пример 82. LINQ-запрос к набору данных DataSet Весьма полезной оказывается организация LINQ-запросов к наборам данных DataSet, используемым, к примеру, при работе с базами данных. Объект класса DataSet представляет расположенный в памяти кэш (cache) данных (кэш — это промежуточная память с быстрым доступом, содержащая информацию, которая может быть запрошена с наибольшей вероятностью). Реляционные базы данных работают чаще всего с совокупностью таблиц. Каждая из этих таблиц задается как объект класса DataTable, один такой объект представляет ровно одну таблицу данных. Набор данных DataSet содержит в себе несколько объектов (таблиц) DataTable. Запросы LINQ к таблицам данных, кэшированным в объекте DataSet, упрощают и ускоряют процесс отбора. Данная задача состоит в том, чтобы создать программу, которая обеспечивает ввод простейшей таблицы, содержащей два поля — название города и численное его населения. Программа способна фильтровать данные в таблице; будем производить отбор городов, численность населения которых превышает миллион жителей. Для решения этой задачи запустим Visual Studio 2010 и выберем проект шаблона Windows Forms Application, укажем имя Name — LinqГopoдa. Далее, попав в конструктор формы, из панели элементов Toolbox перетащим элемент управления для отображения и редактирования табличных данных DataGridView, две командные кнопки Button и текстовое поле TextBox. Одна кнопка предназначена для команды сохранения данных, другая — для поиска данных в таблице, а текстовое поле — для вывода в него найденных строк из таблицы. В свойствах текстового поля разрешим ввод множества строк, для этого свойство Multiline переведем в состояние truе. Затем на вкладке программного кода введем текст, представленный в листинге 11.9. I Листинг 11.9. Извлечение полей из набора данных DataSet //В данной программе экранная форма содержит элемент управления // для отображения и редактирования табличных данных DataGridView, // две командные кнопки и текстовое поле. При старте программы, // если есть соответствующий файл XML, программа отображает //в DataGridView таблицу городов - название города и численность населения. // При щелчке на кнопке "Сохранить" все изменения в таблице записываются // в XML-файл. При щелчке на второй кнопке "Найти" выполняется LINQ-запрос // к набору данных DataSet на поиск городов-миллионеров в искомой таблице. //Pезультат запроса выводится в текстовое поле. using System; using System.Data; usingSystem.Windows.Forms; // Другие директивы using удалены, поскольку они не используются в данной программе namespace _LinqГopoдa { public partial class Form1 : Form { DataTable Таблица = new DataTabie(); // Создание объекта "таблица данных" DataSet НаборДанных = new DataSetО; // Создание объекта "набор данных" public Form1() { InitializeComponent(); base.Text = "LINQ-запрос к набору данных DataSet"; button1.Text = "Сохранить"; button2.Text = "Найти"; textBox1.Multiline = true; if (System.IO.File.Exists("Города.xml") == false) { // Если XML-файла НЕТ: // заполнение "шапки" таблицы Таблица.Columns . Add ("Город") ; Таблица. Columns . Add ("Население"); // Добавить объект Таблица в DataSet: НаборДанных.Таbles.Add(Таблица); dataGridView1.DataSource = Таблица; } else // Если XML-файл ЕСТЬ: { НаборДанных. ReadXml (" Города. xml"); // Содержимое DataSet в виде строки XML для отладки: // string CтрокaXML = НаборДанных.GetXml(); Таблица = НаборДанных.Tables["Города"]; dataGridView1.DataMember = "Города"; dataGridView1.DataSource = НаборДанных; } } private void button1_Click(object sender, EventArgs e) { // Щелчок мышью на кнопке "Сохранить" - сохранить файл Города.xml: Таблица.TableName = "Города"; НаборДанных.WriteXml("Города.xml"); } private void button2_Click(object sender, EventArgs e) { // Щелчок мышью на кнопке "Поиск" - запрос городов-миллионеров: textBoxl.Clear(); // - очистка текстового поля var ГородаМлн = from Город in Таблица.AsEnumerable() where Convert.ToInt32(Город. Field select new { A = Город.Field В = Город.Field }; textBox1. Text = textBox1. Text + "Города-миллионеры: \r\n"; // Вывод результата запроса в текстовое поле textBox1: foreach (var Город in ГородаМлн) textBox1.Text = textBox1.Text + Город.A + " - " + Город.В + "\r\n"; } } } В начале программы создаем объекты классов DataSet и DataTable так, чтобы они были видимыми из всех процедур класса Form1. Далее сразу после инициализации компонентов экранной формы (т. е. после выполнения процедуры InitializeComponent) проверяем, существует ли файл Города.хml, куда мы записываем искомую таблицу. Если файл не существует, т. е. пользователь первый раз запустил нашу программу, то мы создаем таблицу, состоящую из двух полей (колонок): "Город" и "Население", добавляем (Add) эту таблицу в набор данных , а также указываем таблицу в качестве источника данных (DataSource) для сеянных dataGridView1. Если же файл Города.xml уже создан, то мы читаем егo в набор данных DataSet и из него заполняем таблицу данных, а также этот набор данных указываем в качестве источника для сетки данных. При обработке события "щелчок мышью на кнопке" Запись программируем сохранение редактируемой таблицы в файле Города.хml. В процедуре обработки события "щелчок на кнопке" Найти организуем LINQ-запрос к заполненной пользователем таблице DataTable, являющейся представителем DataSet. Условием запроса является отбор таких полей таблицы, где население превышает миллион жителей. Заметим, что если не задавать условие where, то в результате запроса получим все содержимое источника данных, т. е. все строки таблицы городов. Результат запроса выводим в текстовое поле, используя цикл foreach. Фрагмент работы программы показан на рис. 11.13. Убедиться в работоспособности программы можно, открыв решение LinqГopoдa.sln из папки LinqГopoдa. Рис. 11.13. LINQ-запрос к набору данных Пример 83. Доступ к базе данных с помощью LINQ to SQL В данном примере вначале создадим базу данных городов, содержащую два поля: название города и численность его населения. Затем организуем доступ к этой базе данных с помощью LINQ to SQL и создадим запрос на извлечение коллекции городов, численность населения в которых превышает миллион жителей. Эту задачу можно было бы решить, организовав LINQ-запрос через набор данных DataSet, как мы это делали в предыдущем разделе, однако мы хотим продемонстрировать и другой подход. Вначале запустим Visual Studio 2010 и выберем проект шаблона Windows Forms Application, укажем имя Name — LinqToSqlГopoдa. Далее, попав в конструктор формы, из панели элементов Toolbox перетащим элемент управления для отображения и редактирования табличных данных DataGridView, на этот элемент в конечном итоге будет попадать результат запроса. Теперь создадим базу данных SQL Server. Для этого в меню Project (Проект) выберем команду Add New Item (Добавить новый элемент). В появившемся окне выберем элемент База данных, основанная на службах, а в поле Name укажем имя базы данных Города.mdf. Далее в окне мастера настройки источника данных зададим тип модели базы данных— Набор данных. Затем согласимся на сохранение строки подключения в файле конфигурации приложения. Теперь после щелчка на кнопке Готово будет создан пустой набор данных. Этот набор данных Города.mdf теперь будет виден в окне Solution Explorer (Обозреватель решений). Чтобы заполнить этот набор данных, дважды щелкнем мышью по значку Города.mdf; таким образом, мы попадаем в окно Server Explorer/Database Explorer (Обозреватель серверов/Обозреватель баз данных). Здесь в контекстном меню узла таблицы выберем команду Добавить новую таблицу. В результате мы попадаем уже в другое окно — dbo.Table1, где зададим имена двух столбцов: Город и Население (рис. 11.14). Рис. 11.14. Заказ полей таблицы в базе данных При сохранении (<Ctrl>+<S>) пустой таблицы появится запрос на выбор имени для таблицы, здесь мы зададим имя Города. Рис. 11.15. Заполнение таблицы городов в базе данных Теперь будем заполнять сформированную таблицу. Для этого в Обозревателе серверов щелкнем правой кнопкой мыши на узле Города (имя нашей таблицы) и в появившемся контекстном меню выберем команду Показать таблицу данных Теперь в окне Города мы имеем возможность заполнять нашу таблицу (рис. 11.15). Рис. 11.15. Заполнение таблицы городов в базе данных На этом этапе задача создания базы данных и заполнения в ней таблицы городов выполнена. Приступаем к организации запроса к таблице городов. Как уже указывалось ранее, LINQ-запрос можно построить через набор данных DataSet, а можно LINQ-запрос организовать с помощью классов LINQ to SQL. Эти классы сопоставляются с таблицами и представлениями базы данных и называются классами сущностей DataContext. Класс сущности сопоставляется с записью, а отдельные свойства класса сущности сопоставляются с отдельными столбцами, образующими запись. Сказанное, вероятно, звучит запутанно, но практически сводится к перетаскиванию мышью созданной нами таблицы Города из окна Server Explorer/ Database Explorer (Обозреватель серверов/Обозреватель баз данных) на так называемый Object Relational Designer (реляционный конструктор объектов). В результате получим класс сущностей именно для нашей таблицы Города, наследованный от базового класса DataContext, и в тексте нашей программы уже легко сможем строить LINQ-запросы, обращаясь к объекту класса сущностей. Чтобы получить в нашем проекте реляционный конструктор объектов, в меню Project выберем команду Add New Item (Добавить новый элемент), а в появившемся одноименном окне — шаблон (элемент) LINQ to SQL Classes. В поле Name укажем имя файла Сущности.dbml и щелкнем на кнопке Add. Внешний вид реляционного конструктора объектов можно увидеть на рис. 11.16. Теперь, как мы уже говорили, просто перетаскиваем мышью таблицу Города из окна Server Explorer/Database Explorer (Обозреватель серверов/Обозреватель баз данных) на реляционный конструктор объектов. Реляционный конструктор объектов создает классы и применяет специфические для LINQ to SQL атрибуты, чтобы иметь функциональные возможности LINQ to SQL (возможности передачи данных и редактирования, какие имеются у DataContext). А нам остается всего лишь на вкладке программного кода ввести текст, представленный на листинге 11.10. Листинг 11.10. Организация LINQ-запроса к базе данных // Даннoe Windows-приложение состоит из экранной формы и элемента управления BitaGridView // В программе организован LINQ-запрос к базе данных городов с помощью базового класса сущностей DataContext. // Для этого в данную программу добавлен (Project | Add New Item) элемент (шаблон) "Классы LINQ to SQL", // Name=Сущности.dbml. После связывания таблицы "Города" из базы данных с базовым классом сущностей // (путем перетаскивания мышью таблицы из окна Server Explorer/Database Explorer в окно конструктора // Object Relational Designer) автоматически был создан класс СущностиDataContext, производный // (наследованный) от базоваго класса DataContext. С помощью этого класса в данной программе организован // LINQ-запрос к базе данных на получение коллекции (списка) городов, численность населения в которых // превышает миллион жителей. Результат запроса выведен на элемент управления DataGridView. using System; using System. Linq; using System.Windows.Forms ; // Другие директивы using удалены, поскольку они не используются в данной программе namespace LinqToSqlГорода { public partial class Form1: Form { // С помощью объекта базового класса DataContext будем иметь доступ // к таблице базы данных: private СущностиDataContext БД = new СущностиDataContext(); public Form1() { InitializeComponent(); //В результате запроса получаем коллекцию записей из таблицы базы // данных, удовлетворяющей условию where: var ГородаМлн = from города in БД.Города where Convert.ToInt32(города.Население) > 1000000 select города; // или select new { города.Город, города.Население }; // Результат запроса выводим на элемент управления DataGridView, // отображающий табличные данные: dataGridView1.DataSource = ГородаМлн; } } } Фрагмент работы программы показан на рис. 11.17. Убедиться в работоспособности программы можно, открыв реше LinqToSqlГорода.sln папки LinqToSqlГорода. Рис. 11.17. Запрос к базе данных на города-миллионеры |