- •Лабораторная работа 4. Язык linq Задание
- •Синтаксис запроса
- •Синтаксис метода
- •Смешанный синтаксис запроса и метода
- •Пример соединения с использованием простого ключа
- •Пример соединения с использованием составного ключа
- •Пример множественного соединения
- •Пример внутреннего соединения, реализуемого с помощью группового соединения
Лабораторная работа 4. Язык linq Задание
Создать решение, в которое включить библиотеку классов и приложение, использующее классы библиотеки. Классы содержат поля, свойства и методы. Добавление объектов к классам должно происходить пакетно (не интерактивно) при загрузке соответствующей формы
Выполнение запросов LINQ
Выполнение запроса над числовым массивом (варианты 1-6)
Выполнение запроса над строковым массивом (варианты 1-5)
Выполнение запроса над коллекцией-массивом объектов (варианты 1-5)
Выполнение двух запросов (использование операции над множествами и операции соединения) над двумя коллекциями-списками объектов(варианты 1-5)
Индивидуальные варианты
1.- 6вар
1. Найти максимальный элемент
2. Найти сумму элементов, меньших 10
3. Найти произведение четных элементов
4. Найти квадраты нечетных элементов
5. Найти сумму отрицательных элементов
6. Найти минимальный элемент
2 – 5 вар.
1. Подсчитать количество строк, содержащих заданную подстроку
2. Найти строки, начинающиеся с заданного символа
3. Найти в каждой строке число запятых
4.Найти строки, являющиеся изображениями чисел
5. Подсчитать сумму чисел, содержащихся в каждой строке (строка содержит числовые и нечисловые слова)
3 – 5вар..
1.лекарства (название, фирма-производитель, цена за упаковку, количество упаковок). Запрос: Определить общую цену определенного лекарства по всем аптекам
2. издания (индекс, название, месячная стоимость, количество месяцев подписки). Запрос: Определить общую стоимость определенного издания
3.детали (название, фирма-изготовитель, количество модификаций, название материала, масса, стоимость 1 грамма), Запрос: Определить стоимость определенного материала для определенной детали
4.студенты (шифр группы, ФИО, номер зачетки, зарплата отца, зарплата матери, и количество членов семьи). Зарплата: Определить среднюю зарплату, приходящуюся на одного члена семьи, для студентов определенной группы
5.детали (название, материал, название операции, количество деталей, время выполнения).Запрос: Определить среднее время выполнения определенной операции для всех деталей
4 – 5 вар.
1.Студенты (ФИО, номер зачетной книжки) и предметы (название, ФИО преподавателя, название кафедры).
2.сотрудники предприятия(табельный номер, ФИО, паспортные данные, адрес, дата рождения, пол, должность и количество детей), и отделы (наименование, количество закрепленных помещений).
3. товары (наименование, цена единицы) и склады (номер, ФИО кладовщика).
4.изделия (наименование, фирма-изготовитель)детали (наименование, фирма-изготовитель, (наименование материала, цена за грамм).
5. книги (регистрационный номер, количество страниц, год издания, раздел - учебник, художественная общественно-политическая и т.д. ) и читатели (ФИО, домашний адрес, паспортные данные).
LINQ
Язык интегрированных запросов LINQ (Language-IntegratedQuery) — новое расширение языка С#, добавленное в версию С# 3.0. Язык LINQ решает проблему работы с большими коллекциями объектов, когда обычно приходится выбирать подмножество коллекции для решения определенной задачи.
В прошлом такого рода работа требовала написания объемного циклического кода и дополнительной обработки, связанной с сортировкой или группированием найденных объектов, которая порождала еще больший код. Язык LINQ избавляет от необходимости 333писать дополнительный циклический код для фильтрации и сортировки. Он позволяет сосредоточиться на объектах, которыми оперирует программа, предоставляя удобные средства формулирования запросов.
Цикл foreach
Цикл foreach используется для перебора элементов коллекции. Коллекция — это группа объектов. С# определяет несколько типов коллекций, и одним из них является массив.
Формат записи цикла foreach имеет такой вид:
foreach(тип имя_переменнойin коллекция) операторы; Здесь элементы тип и имя_переменной задают тип и имя итерационной переменной, которая при функционировании цикла fоreach будет получать значения элементов из коллекции. Элемент коллекция служит для указания просматриваемой коллекции (в данном случае в качестве коллекции мы рассматриваем массив). Таким образом, элемент тип должен совпадать (или быть совместимым) с базовым типом массива. Здесь важно запомнить, что итерационную переменную применительно к массиву можно использовать только для чтения. Следовательно, невозможно изменить содержимое массива, присвоив итерационной переменной новое значение.
Рассмотрим простой пример использования цикла foreach. Приведенная ниже программа создает массив для хранения целых чисел и присваивает его элементам начальные значения. Затем она отображает элементы массива, попутно вычисляя их сумму. // Использование цикла foreach. usingSystem; classForeachDemo { publicstaticvoidMainO{ intsum = 0; int[] nums = newint[10];
// Присваиваем элементам массива nums значения,
for(int i = 0; i < 10; i++) nums[i] = i ;
// Используем цикл foreach для отображения значений
// элементов массива и их суммирования, foreach(int x innums) { Console.WriteLine("Значение элемента равно: " + х) ; sum += х;Console.WriteLine("Сумма равна: " + sum);
Как видно из приведенных выше результатов, цикл foreach последовательно просматривает элементы массива в направлении от наименьшего индекса к наибольшему. Несмотря на то, что цикл foreach работает до тех пор, пока не будут просмотрены все элементы массива, существует возможность досрочного его останова с помощью оператора break. Например, следующая программа суммирует только пять первых элементов массива nums.
classForeachDemo {
public static void MainO
{ int sum = 0; int[] nums = new int [10];
// Присваиваем элементам массива nums значения,
for(int i = 0; i < 10; i++)
nums[i] = i;// Используем цикл foreach для отображения значений
// элементов массива и их суммирования,
foreach(int x in nums)
{Console.WriteLine("Значение элемента равно: " + х) ;sum += х;if(x == 4) break; }
// Останов цикла, когда х равен 4.
Console.WriteLine("Сумма первых 5 элементов: " + sum);
Поскольку цикл fоreach может просматривать массив последовательно (от начала к концу), может сложиться впечатление, что его использование носит весьма ограниченный характер. Однако это не так. Для функционирования широкого круга алгоритмов требуется именно такой механизм. Одним из них является алгоритм поиска.
Например, следующая программа использует цикл foreach для поиска в массиве заданного значения.
Когда значение найдено, цикл останавливается.
//Поиск значения в массиве с помощью цикла foreach. usingSystem;
class Search (public static void MainO {
int[] nums = new int[10];
intval; bool found = false;// Присваиваем элементам массива numsзначения,
for(inti - 0; i< 10; I++)
nums [ i ] - i;
val = 5;// Используем цикл foreach для поиска в массиве nums
// заданного значения,
foreach(int x in nums) { if(x == val) ( found = true; break; }
if(found) Console.WriteLine("Значение найдено!");
Цикл foreach также используется для вычисления среднего значения, определения минимального или максимально числа в наборе чисел, поиска дубликатов и т.д.
Пример foreach с массивом строк
string[] sotr = { "Иванов", "Петров", "Сидоров", "Сергеев", "Миронов", "Сидоров" };
int k = 0;
foreach (string s insotr)
{ Console.WriteLine(s);
if (s == "Сидоров")
k++;
}
Console.WriteLine("Имеется {0} Сидоровых", k);
Console.ReadKey();
Классы коллекций
Объекты, то есть экземпляры классов, полезно рассматривать в их совокупности. Это позволяет выполнять некоторые действия по суммированию значений полей, подсчету количества экземпляров, удовлетворяющих определенным условиям, и выполнению самых разнообразных запросов. Возникает вопрос, как хранить такую совокупность объектов?
Простым способом хранения совокупности элементов является массив. Он позволяет добавлять к нему элементы, удалять элементы, просматривать его для выполнения различных запросов. В один массив, объявленный с типом базового класса, можно помещать объекты базового класса и дочерних классов. Например,
Global.A1 = new stud[20];
С массивами, однако, связаны свои ограничения. Наибольшее из них состоит в том, что после создания массивы имеют фиксированный размер, так что нельзя добавлять новые элементы в конец существующего массива без создания нового.
Коллекции также позволяют обслуживать группы объектов. Коллекции могут предоставлять более совершенную функциональность, вроде возможности управлять доступом к содержащимся в них объектам, возможности выполнять по ним поиск и т.д.
Цикл foreach позволяет обращаться к каждому элементу в коллекции с помощью такого простого синтаксиса:
foreach( <базовый_тип><имя>in<коллекция> )
{ // можно использовать <имя> для каждого элемента }
Этот цикл будет осуществлять проход по всем элементам и помещать каждый из них по очереди в переменную <имя> безо всякого риска получения доступа к недопустимым элементам. Такой подход позволяет не беспокоиться о том, сколько элементов содержится в коллекции, и быть уверенным, что каждый из них будет использован в цикле.
Аналогично массивам необходимо выполнять приведение типов для использования методов класса-потомка.
При использовании коллекций в форму надо вставлять директиву
usingSystem.Collections;
Класс List и его методы
Класс List – это коллекция, элементами которой является экземпляры класса. Поддерживает методы для поиска по списку, выполнения сортировки и других операций со списками.
Объявление списка имеет вид:
List<тип входящего объекта> имя = newList< тип входящего объекта>();
У класса имеются свойство
Count - Число элементов, которое фактически содержится в коллекции.
Полезные методы класса List перечислены в табл. 12.
Таблица 12
Некоторые методы класса List
Метод |
Описание |
Add(объект) |
Добавляет объект в конец коллекции |
Clear() |
Удаляет все элементы из коллекции |
bool Contains(элемент)
|
Определяет, входит ли элемент в состав коллекции |
bool Exists(метода)
|
Определяет, содержит ли коллекция элементы, удовлетворяющие условиям указанного метода, который в качестве параметра должен иметь объект того класса, который помещен в коллекцию |
Тип объекта Find(метод)
|
Выполняет поиск элемента, удовлетворяющего условиям метода, и возвращает первое найденное вхождение в пределах всего списка |
Insert(индекс с 0,элемент) |
Добавляет элемент в список в позиции с указанным индексом |
bool Remove(элемент)
|
Удаляет первое вхождение указанного объекта из коллекции |
Sort(метод сравнения)
|
Сортирует элементы во всем списке с использованием указанного метода |
Один из примеров иллюстрирует применение метода Exists
List<string> dinosaurs = new List<string>();
dinosaurs.Add("Compsognathus");
dinosaurs.Add("Amargasaurus");
dinosaurs.Add("Oviraptor");
dinosaurs.Add("Velociraptor");
dinosaurs.Add("Deinonychus");
dinosaurs.Add("Dilophosaurus");
dinosaurs.Add("Gallimimus");
dinosaurs.Add("Triceratops");
Console.WriteLine("\nExists(EndsWithSaurus): {0}",
dinosaurs.Exists(EndsWithSaurus));
// Метод поиска возвращает true, если строка заканчивается "saurus"
private static boolEndsWithSaurus(String s)
{ if ((s.Length> 5) && (s.Substring(s.Length - 6).ToLower() == "saurus"))
{ return true; }
else
{return false; } }
Второй пример иллюстрирует применение метода Sort:
List<string> dinosaurs = new List<string>();
dinosaurs.Add("Pachycephalosaurus");
dinosaurs.Add("Amargasaurus");
dinosaurs.Add("");
dinosaurs.Add(null);
dinosaurs.Add("Mamenchisaurus");
dinosaurs.Add("Deinonychus");
Display(dinosaurs);
dinosaurs.Sort(CompareDinosByLength);
Display(dinosaurs);
private static intCompareDinosByLength(string x, string y)
{// сранениедлиндвухстрок
intretval = x.Length.CompareTo(y.Length);
if (retval != 0)
// Если строки неравной длины, то более длинная строка больше
returnretval;
else
{// Если строки равной длины, они сортируются обычным образом
returnx.CompareTo(y); }
}
private static void Display(List<string> list)
{ Console.WriteLine();
foreach( string s in list )
{if (s == null)
Console.WriteLine("(null)");
else
Console.WriteLine("\"{0}\"", s); } }
LINQ для массивов
Первый запрос LINQ
В следующем примере создадим запрос для поиска некоторых данных в простом, находящемся в памяти, массиве объектов с использованием LINQ и выведем результат на форму.
В число директив надо добавить
usingSystem.Linq;
Форма имеет вид (рис. )
Рис. Форма приложения для поиска
В нижней части формы расположена компонента label для вывода результатов поиска.
Фактически можно выбрать один из двух запросов –
поиск данных, содержащих заданную строку и
поиск данных, равных заданной строке.
Далее создадим некоторые данные, что осуществляется
объявлением и инициализацией массива names:
string[] names = { "Alonso", "Zheng", "Smith", "Jones", "Smythe", "Small", "Ruiz", "Hsieh", "Jorgenson", "Ilyich", "Singh", "Samba", "Fatimah" };
При нажатии кнопки выбирается и выполняется один из двух вариантов запроса:
if (radioButton1.Checked)
{ // поискподстроки
var gueryResults1 =
from n in names
where n.Contains(aName)
select n;
label1.Text = "";
foreach (string st in gueryResults1)
label1.Text = label1.Text + st + '\n'; }
else
{ // равенство
var gueryResults2=
from n in names
where n.Equals(aName)
select n;
label1.Text = "";
foreach (string st in gueryResults2)
label1.Text = label1.Text + st + '\n'; }
Оператор запроса LINQ состоит из четырех частей: начинающееся с var объявление переменной gueryResults1, которой выполняется присваивание с использованием синтаксиса запроса, состоящего из конструкций from, where и select.
Ключевое слово var заставляет компилятор С# вывести тип результата на основе самого запроса. Таким образом, не нужно заранее объявлять тип объектов, возвращаемых запросом LINQ— компилятор позаботится об этом сам. Если запрос может вернуть множество элементов, то этот тип должен вести себя как коллекция объектов из источника данных запроса;
Конструкция from специфицирует источник запрашиваемых данных:
from n in names
Источником данных в рассматриваемом случае является names — массив строк, объявленный ранее. Переменная n — просто описание индивидуального элемента в источнике данных - массиве.
В следующей части запроса определяетя условие запроса с использованием конструкции where:wheren.Equals(aName).В конструкции where может быть определено любое булевское выражение, которое применимо к элементам в источнике данных. Конструкция where называется ограничивающей операцией в LINQ, потому что она ограничивает результат запроса.
В примере определено, что строка равна заданной.
Конструкция select определяет, какие элементы появятся в результирующем наборе. В примере она выглядит так: select n
Конструкция select необходима для определения того, какие элементы запрос поместит в результирующий набор.
Формирование результата происходит с помощью цикла foreach:
foreach (string st in gueryResults2)
label1.Text = label1.Text + st + '\n';
В LINQ существует много способов решения одной и той же задачи, как это часто бывает в программировании. Предыдущий пример написан с использованием синтаксиса запросов LINQ. В следующем примере напишем ту же программу с применением синтаксиса методов LINQ.
Если написать в программе names и поставить точку после names, то можно увидеть в списке все доступные методы объекта names, как показано на рис.
Рис. Список методов объекта names
Метод Where<> и большинство прочих доступных методов являются расширениями LINQ. Выражение запроса for. . .where. . .select, использованное в предыдущем примере, транслируется компилятором С# в серию вызовов этих методов. При использовании синтаксиса методов LINQ эти методы вызываются напрямую. Синтаксис запросов является предпочтительным способом программирования запросов в LINQ, поскольку его намного легче читать и проще использовать в большинстве часто применяемых запросов. Однако важно иметь базовое понятие о синтаксисе методов, поскольку некоторые возможности LINQ либо недоступны в синтаксисе запросов, либо просто их легче применять в синтаксисе методов.
Большинство методов LINQ, использующих синтаксис методов, требует передачи метода или функции для вычисления выражения запроса. используя специальную конструкцию С# 3.0, которая называется лямбда -выражением. Это короткий, согласованный способ записи метода, применяемый для вычисления выражений запроса. Ниже показано простое лямбда-выражение:
п =>п< 1000
Операция => называется лямбда - операцией.
Лямбда-выражение n => n < 1000 определяет функцию, которая принимает параметр по имени n и возвращает true, если п меньше 1000, и false— в противном случае.
Лямбда-выражение для запроса в примере программы может быть
записано, как
n =>n.Equals(aName);
что следует читать, как "n такое, что оно равно aName”.
Изменение в примере:
var gueryResults1 =
names.Where(n=>n.Contains(aName));
label1.Text = "";
foreach (string st in gueryResults1)
label1.Text = label1.Text + st + '\n';
В конструкции where (или вызове метода Where ()), LINQ позволяет сортировать результирующие данные.
Для этого существует ключевое слово orderby:
var gueryResults2=from n in names where n.Equals(aName) orderby n select n;
Это сортировка по возрастанию. Для сортировки по убыванию добавляется слово descending:
where n.Equals(aName) orderby n descending
Для отбора данных, удовлетворяюших учловию, можно применять операции сравнения. Например:
int[] numbers = generateLotsOfNumbers(12345678);
varqueryResults = from n in numbers where n < 1000 select n ;
foreach (int item in queryResults)
{ ListBox1.Items.Add(item.ConvertTo.String
Console.WriteLine(item); }
generateLotsOfNumbers - методдлягенерацииспискаслучайныхчисел:
private static int[] generateLotsOfNumbers(int count)
{ Random generator = new Random();
int[] result = new int[count];
for (inti = 0; i< count; i++)
{ result[i] = generator.Next (); }
return result; }
LINQ предоставляет набор агрегатных операций, позволяющих анализировать результаты запроса, не выполняя цикл по всем результатам. Перечисленные ниже агрегатные операции чаще всего используются для набора числовых результатов, таких как результаты предыдущего запроса.
Count () Подсчитывает количество результатов
Min () Минимальное значение результатов
Mах () Максимальное значение результатов
Average () Среднее значение числовых результатов
Sum () Сумма всех числовых результатов
Приведенный ниже код выводит в список различные числовые характеристики подмножества запроса:
int[] numbers = generateLotsOfNumbers(2345678);
listBox1.Items.Add("Числовыеагрегаты");
var queryResults = from n in numbers where n > 1000 select n;
listBox1.Items.Add("Количество чисел > 1000");
listBox1.Items.Add(Convert.ToString(queryResults.Count()));
listBox1.Items.Add("Максимальное из чисел > 1000");
listBox1.Items.Add(Convert.ToString(queryResults.Max()));
listBox1.Items.Add("Минимальное из чисел > 1000");
listBox1.Items.Add(Convert.ToString(queryResults.Min()));
listBox1.Items.Add("Среднее из чисел > 1000");
listBox1.Items.Add(Convert.ToString(queryResults.Average()));
listBox1.Items.Add("Суммачисел> 1000");
listBox1.Items.Add(Convert.ToString(queryResults.Sum(n => (long)n)));
Все операции запроса LINQ состоят из трех различных действий.
Получение источника данных.
Создание запроса.
Выполнение запроса.
В следующем примере показано выражение этих трех частей операции запроса в исходном коде. В примере в качестве источника данных для удобства используется массив целых чисел; тем не менее, те же принципы применимы и к другим источникам данных.
classIntroToLINQ
{
staticvoid Main()
{
// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = newint[7] { 0, 1, 2, 3, 4, 5, 6 };
// 2. Query creation.
// numQuery is an IEnumerable<int>
varnumQuery =
from num in numbers
where (num % 2) == 0
select num;
// 3. Query execution.
foreach (intnuminnumQuery)
{
Console.Write("{0,1} ", num);
}
}
}
На следующем рисунке показана завершенная операция запроса. В LINQ выполнение запроса отличается от самого запроса; другими словами, создание переменной запроса само по себе не связано с получением данных.
Запрос указывает, какую информацию нужно извлечь из источника или источников данных. При необходимости, запрос также указывает способ сортировки, группировки и формирования этих сведений перед возвращением. Запрос хранится в переменной запроса и инициализируется выражением запроса. Чтобы упростить написание запросов, в C# появился новый синтаксис запроса.
Запрос из первого примера возвращает все четные числа из массива целых чисел. Выражение запроса содержит три предложения: from, where и select. (Если вы знакомы с SQL, обратите внимание, что порядок предложений противоположен порядку в SQL.) Предложение from указывает источник данных, предложение where применяет фильтр, а предложение select указывает тип возвращаемых элементов.
Важно, что в LINQ сама переменная запроса не предпринимает действий и не возвращает никаких данных.Она просто хранит сведения, необходимые для предоставления результатов при последующем выполнении запроса.
В этом разделе рассматриваются три способа создания запросов LINQ на языке C#.
Использование синтаксиса запроса.
Использование синтаксиса метода.
Использование сочетания синтаксиса запроса и синтаксиса метода.
В следующем примере демонстрируются простые запросы LINQ при использовании каждого из перечисленных выше синтаксисов. Общее правило таково: следует использовать (1) всегда, когда это возможно; (2) и (3) при необходимости.
