Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ТЕХНОЛОГИЯ ПРОГРАММИРОВАНИЯ-Лабораторная 4.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
182.96 Кб
Скачать

Синтаксис запроса

Лучше всего для создания запросов использовать синтаксис запроса, создавая выражения запросов. В следующем примере показано три выражения запроса.В первом выражении демонстрируется фильтрация или ограничение результатов путем применения условий в предложении where.Оно возвращает все элементы в исходной последовательности со значениями больше 7 и меньше 3. Второе выражение демонстрирует сортировку возвращаемых результатов. Третий запрос демонстрирует группировку результатов. Он возвращает две группы на основе первой буквы слова.

// Query #1.

List<int> numbers = new List<int>() { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

// The query variable can also be implicitly typed by using var

varfilteringQuery =

from num in numbers

where num < 3 || num > 7

select num;

// Query #2.

varorderingQuery =

from num in numbers

where num < 3 || num > 7

orderbynumascending

select num;

// Query #3.

string[] groupingQuery = { "carrots", "cabbage", "broccoli", "beans", "barley" };

varqueryFoodGroups =

from item ingroupingQuery

groupitembyitem[0];

В каждом из приведенных выше примеров фактическое выполнение запроса откладывается до использования переменной запроса в операторе foreach.

Синтаксис метода

Некоторые операции запросов должны быть выражены в виде вызова метода. Чаще всего используются методы, возвращающие одноэлементные числовые значения, например Sum, Max, Min, Average и т.д.Эти методы всегда должны быть вызваны последними в запросе, поскольку они представляют только одно значение и не могут служить источником дополнительных действий запроса. В следующем примере демонстрируется вызов метода в выражении запроса.

List<int> numbers1 = new List<int>() { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

List<int> numbers2 = new List<int>() { 15, 14, 11, 13, 19, 18, 16, 17, 12, 10 };

// Query #4.

double average = numbers1.Average();

// Query #5.

var concatenationQuery = numbers1.Concat(numbers2);

Если у метода есть параметры, они представлены в виде лямбда-выражения, как показано в следующем примере.

// Query #6.

var largeNumbersQuery = numbers2.Where(c => c > 15);

Смешанный синтаксис запроса и метода

В этом примере демонстрируется использование синтаксиса метода для результатов предложения запроса. Нужно всего лишь заключить выражение запроса в скобки, а затем применить оператор точки и вызвать метод. В следующем примере запрос #7 возвращает количество чисел, значение которых лежит в диапазоне от 3 до 7.Однако в общем случае лучше использовать вторую переменную для хранения результатов вызова метода. Таким образом, будет меньше вероятность перепутать запрос с результатами запроса.

// Query #7.

// Using a query expression with method syntax

int numCount1 =

(from num in numbers1

where num < 3 || num > 7

select num).Count();

// Better: Create a new variable to store

// the method call result

мar numbersQuery =

from num in numbers1

where num < 3 || num > 7

select num;

int numCount2 = numbersQuery.Count();

Запрос №7 возвращает одиночное значение, а не коллекцию, поэтому он выполняется мгновенно.

Можно использовать синтаксис метода следующим образом.

var numCount = numbers.Where(n => n < 3 || n > 7).Count();

Можно использовать явную типизацию следующим образом.

Int numCount = numbers.Where(n => n < 3 || n > 7).Count();

LINQ для коллекций классов

Создадим класс Customer и коллекцию типа List, содержащую экземпляры Customer.

В классе Customer есть поля, конструктор, методы Get_Region(0 и Get_ID() для возврата значений соответствующих полей и метод Info() для отображения полей экземпляра класса:

class Customer

{public string ID ;

private string City ;

private string Country ;

private string Region ;

private decimal Sales ;

public string Get_Region() { return Region; }

public Customer(string ID, string City, string Country, string Region, decimal Sales)

{this.ID = ID; this.City = City; this.Country = Country;

this.Region = Region; this.Sales = Sales;}

public string Info()

{ return "ID: " + ID + " Город: " + City + " Страна: " + Country +

"Регион: " + Region + " Продажи: " + Convert.ToString(Sales); } }

List<Customer> customers = new List<Customer>();

При загрузке формы добавим к коллекции несколько объектов:

customers.Add(new Customer("A", "New York", "USA","North America", 9999));

customers.Add(new Customer("C", "Karachi", "Pakistan", "Asia", 7777));

customers.Add(new Customer("D", "Delhi", "India", "Asia", 6666));

customers.Add(new Customer("E", "SantaPaulo", "Brazil", "South America", 5555));

customers.Add(new Customer("F", "Moscow", "Russia", "Europe", 4444));

customers.Add(new Customer("G", "Seoul", "Korea", "Asia", 3333));

customers.Add(new Customer("H", "Istanbul", "Turkey", "Asia", 2222));

customers.Add(new Customer("I", "Shanghai", "China", "Asia", 1111));

customers.Add(new Customer("J", "Lagos", "Nigeria", "Africa", 1000));

customers.Add(new Customer("K", "Mexico City", "Mexico", "North America", 1000));

customers.Add(new Customer("LM", "Jakarta", "Indonesia", "Asia", 3000));

customers.Add(new Customer("M", "Tokyo", "Japan", "Asia", 4000));

customers.Add(new Customer("N", "Los Angeles", "USA", "North America", 5000));

customers.Add(new Customer("O", "Cairo", "Egypt", "Africa", 6000));

customers.Add(new Customer("P", "Tehran", "Iran", "Asia", 7000));

customers.Add(new Customer("Q", "London", "UK", "Europe", 8000));

customers.Add(new Customer("R", "Beijing", "China", "Asia", 9000));

customers.Add(new Customer("S", "Bogota", "Colombia", "South America", 1001));

customers.Add(new Customer("T", "Lima", "Peru", "South America", 2002));

На форме поместим кнопку и список listBox для вывода объектов из Азии при нажатии на кнопку (рис. ):

Рис. Форма для вывода части коллекции

Код события нажатия кнопки:

private void button1_Click(object sender, EventArgs e)

{var queryResults = from c in customers where c.Get_Region() == "Asia" select c;

listBox1.Items.Clear();

foreach (Customer d in queryResults)

listBox1.Items.Add(d.Info()); }

Переменная типа var содержит результаты запроса для коллекции List с условием отбора экземпляров из Азии. Цикл foreach добавляет в список ListBox значение метода Info (рис. )

Рис. Форма при выполнении приложения

Для применения нужного числа выражений фильтра в предложении where можно использовать логические операторы C# && и ||.

В предложении where может содержаться один или несколько методов, возвращающих логические значения.

Например,

varqueryEvenNums =from num in numbers where IsEven(num)select num;

// Выполнениезапроса

foreach (vars inqueryEvenNums)

{Console.Write(s.ToString() + " "); } }

// Метод.

staticboolIsEven(inti)

{ return i % 2 == 0; }

Включение в запрос дополнительных полей

В LINQ можно создать новый объект в конструкции select для хранения результатов, которые нужны для запроса.

Модифицируем код нажатия кнопки:

var queryResults =

from c in customers select new { Value = c.Get_Region(), Id = c.Get_ID() };

foreach (var d in queryResults)

{ string v = d.Value;string id = d.Id;

listBox1.Items.Add(v+" ***"+id);}

Выбор уникальных значений

Еще один тип запросов — запрос selectdistinct, в котором выполняется поиск уникальных значений в данных, то есть значений, которые не повторяются. Потребность в этом возникает очень часто при работе с запросами.

Предположим, что нужно извлечь список регионов из данных заказчиков, которые применялись в предыдущих примерах. В используемых данных нет отдельного списка регионов, поэтому необходимо получить уникальный, не повторяющийся список регионов из списка заказчиков. LINQ предоставляет метод Distinct(), который облегчает выполнение задачи нахождения данных подобного рода.

Например,

var queryResults =(from c in customers select c.Get_Region()).Distinct();

Запросы ANY и ALL

Еще один тип запросов состоит в определении того, что какие-то данные удовлетворяют определенному условию, или в определении того, что все данные удовлетворяют условию. Например, может потребоваться знать, что продукта нет на складе (количество равно нулю) или что транзакция произошла.

LINQ предоставляет булевские методы Any() и All(), которые могут быстро сообщить, истинно ли некоторое условие для существующих данных.

Например, поместим на форму кнопки для определения того, что есть заказчики из США и из Азии.

Код, выполняемый при нажатии на ту и другую кнопки, следующий:

private void button2_Click(object sender, EventArgs e)

{ bool anyUSA = customers.Any(с =>с.Get_Country() == "USA");

if (anyUSA)

label1.Text = "Некоторые заказчики находятся в США";

else

label1.Text = "Нет заказчиков из США"; }

private void button6_Click(object sender, EventArgs e)

{bool allAsia = customers.All(с =>с.Get_Region() == "Asia");

if (allAsia)

label2.Text="Все заказчики находятся в Азии";

else

label2.Text="He все заказчики находятся в Азии"; }

Групповой запрос

Групповой запрос делит данные на группы и позволяет сортировать, вычислять агрегатные значения и сравнивать группы.

В код события нажатия кнопки Сумма заказов по каждому региону поместим два запроса и цикл обработки результатов:

var queryResults =

from с in customers group с by с.Get_Region() into eg

select new { TotalSales = eg.Sum(c => c.Get_Sales()), Region = eg.Key };

var orderedResults =

from eg in queryResults

orderby eg.TotalSales descending

select eg;

foreach (var item in orderedResults)

{listBox2.Items.Add(item.TotalSales + "\t: " + item.Region);

Данные в групповом запросе группируются по ключевому полю — полю, для которого все члены каждой группы имеют одноименное значение – сумма продаж. В этом примере ключевым полем является Region:

group с by с.Get_Region()

Необходимо вычислить сумму по каждой группе, поэтому выполняется группирование в новый результирующий набор по имени eg:

group с by с.Get_Region() into eg

В конструкции select определяются новые поля — общая сумма продаж (вычисляемая путем сылки на результирующий набору eg) и ключевое значение группы, к которому обращение происходит по специальному полю Key группы:

selectnew { TotalSales = eg.Sum(c => с. Get_Sales()), Region = eg.Key }.это критерий создания каждой группы данных.

Желательно упорядочить результат в порядке убывания по полю TotalSales. Для этого создается второй запрос для упорядочивания результатов группового запроса. Это стандартный запрос select с конструкцией orderby; он не использует никаких групповых средств LINQ, за исключением того, что источник данных для него поступает от предыдущего группового запроса.

Затем результаты второго запроса выводятся в список.

Вложенная группировка

В следующем примере показано, как создавать вложенные группы в выражении запроса LINQ. Каждая группа, созданная в соответствии с годом или уровнем обучения, затем подразделяется на группы на основе имен учащихся.

public void QueryNestedGroups()

{

var queryNestedGroups =

from student in students

group student by student.Year into newGroup1

from newGroup2 in

(from student in newGroup1

group student by student.LastName)

group newGroup2 by newGroup1.Key;

// Three nested foreach loops are required to iterate

// over all elements of a grouped group. Hover the mouse

// cursor over the iteration variables to see their actual type.

foreach (varouterGroupinqueryNestedGroups)

{Console.WriteLine("DataClass.Student Level = {0}", outerGroup.Key);

foreach (varinnerGroupinouterGroup)

{Console.WriteLine("\tNames that begin with: {0}", innerGroup.Key);

foreach (varinnerGroupElementininnerGroup)

{Console.WriteLine("\t\t{0} {1}", innerGroupElement.LastName, innerGroupElement.FirstName);

} } }}

/*

Output:

DataClass.Student Level = SecondYear

Names that begin with: Adams

Adams Terry

Names that begin with: Garcia

Garcia Hugo

Names that begin with: Omelchenko

Omelchenko Svetlana

DataClass.Student Level = ThirdYear

Names that begin with: Fakhouri

FakhouriFadi

Names that begin with: Garcia

Garcia Debra

Names that begin with: Tucker

Tucker Lance

DataClass.Student Level = FirstYear

Names that begin with: Feng

FengHanying

Names that begin with: Mortensen

Mortensen Sven

Names that begin with: Tucker

Tucker Michael

DataClass.Student Level = FourthYear

Names that begin with: Garcia

Garcia Cesar

Names that begin with: O'Donnell

O'Donnell Claire

Names that begin with: Zabokritski

ZabokritskiEugene

*/

Обратите внимание на то, что для выполнения итерации по внутренним элементам вложенной группы необходимы три вложенных цикла foreach.

Take и Skip

В ряде случаев нужно найти несколько первых данных из запроса. Их количество заранее неизвестно, поэтому нельзя использовать условие where для их поиска.

LINQ-операция такого запроса представлена методом Take (), который

учитывает первые n результатов при выводе запроса. При практическом применении это может быть скомбинировано с orderby для получения первых n результатов.

Противоположностью Take () является метод Skip (), который пропускает первые n результатов, возвращая остальные.

Предположим, что добавлено две кнопки – выбор первого десятка продаж с наибольшими суммами и выдача всех продаж, кроме первого десятка.

Запрос:

var queryResults =

from c in customers

orderby c.Get_Sales() descending select new {Dan=c.Info()};

Опишем два цикла обработки результатов, один с использованием Take (), а другой — с применением Skip ():

foreach (var item in queryResults.Take(10))

{ listBox2.Items.Add(item.Dan); }

foreach (var item in queryResults.Skip(10)) { listBox2.Items.Add(item.Dan);}

First и FirstOrDefault

Предположим, что требуется найти заказчика из Африки в наборе данных.

LINQ предоставляет эту возможность через метод First(), который возвращает первый элемент результирующего набора, отвечающий заданным критериям. Если нет ни одного заказчика, например, из Антарктиды, то LINQпредоставляет метод для обработки такого случая без дополнительного кода обработки ошибок: FirstOrDefault():

var queryResults = from c in customers

select new{ID=c.Get_ID(),Region=c.Get_Region()} ;

listBox1.Items.Add(queryResults.First(c => c.Region == "Africa"));

listBox1.Items.Add(queryResults.FirstOrDefault(c => c.Region == "Antarctica"));

Операции с множествами

В LINQ операциями с множествами называют операции запроса, образующие результирующий набор, основанный на наличии или отсутствии эквивалентных элементов внутри одной или разных коллекций (или множеств).

В следующем разделе перечислены методы стандартных операторов запросов, выполняющие операции с множествами.

Имя метода

Описание

Distinct

Удаляет повторяющиеся значения из коллекции.

Except

Возвращает разность множеств — набор элементов одной коллекции, которые не содержатся во второй коллекции.

Intersect

Возвращает пересечение множеств — набор элементов, содержащихся в каждой из коллекций.

Union

Возвращает объединение множеств — набор уникальных элементов, содержащихся в любой из коллекций.

Сравнение операций над множествами

Distinct

На следующем рисунке показано действие метода Distinct в последовательности знаков. Возвращаемая последовательность содержит уникальные элементы из входной последовательности.

Except

На следующем рисунке показано действие метода Except. Возвращаемая последовательность содержит только те элементы из первой входной последовательности, которые не содержатся во второй входной последовательности.

Intersect

На следующем рисунке показано действие метода Intersect. Возвращаемая последовательность содержит элементы, общие для обеих входных последовательностей.

Union

На следующем рисунке показана операция объединения двух последовательностей знаков. Возвращаемая последовательность содержит уникальные элементы из обеих входных последовательностей.

LINQ поддерживает стандартные операции множеств, такие как Union () и Intersect () - объединентие и пересечение, оперирующие результатами запроса.

В примере добавим список заказов и используем операции над множествами для сопоставления заказов с существующими закшзчиками.

Создадимновыйкласс:

class Order

{ string ID; decimal Amount;

public string Get_ID() { return ID; }

public decimal Get_Amount() { return Amount; }

public Order(string ID, decimal Amount)

{ this.ID = ID; this.Amount = Amount;}

public string Info()

{ return "ID: " + ID + " Сумма: " + Convert.ToString(Amount); }

Создадим коллекцию объектов класса Order и поместим в нее экземпляры:

List <Order> orders = new List<Order>();

orders.Add(new Order("P", 100 ));

orders.Add(new Order ("Q", 200 ));

orders.Add(new Order ("R",300));

orders.Add(new Order ("S",400));

orders.Add(new Order ("T",500));

orders.Add(new Order ("MU", 600));

orders.Add(new Order ("V",700));

orders.Add(new Order ("MW",800));

orders.Add(new Order ("X",900));

orders.Add(new Order ("Y",1000));

orders.Add(new Order ("Z",1100));

Создадим запросы по каждой коллекции:

var customerlDs =

from с in customers

select с.Get_ID();

var orderlDs =

from o in orders

select o.Get_ID();

Далее создадим три запроса для выполнения соответственно операций пересечения, исключения и объединения:

var customersWithOrders = customerlDs.Intersect(orderlDs);

listBox1.Items.Clear();

foreach (var item in customersWithOrders)

{listBox1.Items.Add(item); }

var ordersNoCustomers = orderlDs.Except(customerlDs);

listBox1.Items.Clear();

foreach (var item in ordersNoCustomers)

{ listBox1.Items.Add(item);}}

var allCustomerOrderlDs = customerlDs.Union(orderlDs);

listBox1.Items.Clear();

foreach (var item in allCustomerOrderlDs)

{listBox1.Items.Add(item);}}

Соединения

Наборы данных, подобные спискам customers и orders, которые были только что созданы с общим ключевым полем (ID) позволяют использовать запрос join, посредством которого можно запросить взаимосвязанные данные из обоих списков в единственном запросе, объединяя их по значению ключевого поля.

var queryResults =

from c in customers

join o in orders on c.Get_ID() equals o.Get_ID()

select new { ID=c.Get_ID(), City=c.Get_City(), SalesBefore = c.Get_Sales(),

NewOrder = o.Get_Amount(), SalesAfter = c.Get_Sales()+o.Get_Amount() };

listBox1.Items.Clear();

foreach (var item in queryResults)

{listBox1.Items.Add(item.ID+" "+item.City+" "+Convert.ToString(item.SalesBefore)+" "

+Convert.ToString(item.NewOrder)+" "+Convert.ToString(item.SalesAfter));}

Операнды equals – это сравниваемые значения ключа в двух списках.

Вид формы при выполнении и нажатии кнопки Соединение (рис. 45):

Рис. 45. Выполнение запроса Соединение

 Во всех примерах в данном разделе используются следующие вспомогательные классы и источники данных.

publicclassStudentClass

{

#region data

protectedenumGradeLevel { FirstYear = 1, SecondYear, ThirdYear, FourthYear };

protectedclass Student

{

publicstringFirstName { get; set; }

publicstringLastName { get; set; }

publicint ID { get; set; }

publicGradeLevel Year;

public List<int>ExamScores;

}

protectedstatic List<Student> students = new List<Student>

{

new Student {FirstName = "Terry", LastName = "Adams", ID = 120,

Year = GradeLevel.SecondYear,

ExamScores = new List<int>{ 99, 82, 81, 79}},

new Student {FirstName = "Fadi", LastName = "Fakhouri", ID = 116,

Year = GradeLevel.ThirdYear,

ExamScores = new List<int>{ 99, 86, 90, 94}},

new Student {FirstName = "Hanying", LastName = "Feng", ID = 117,

Year = GradeLevel.FirstYear,

ExamScores = new List<int>{ 93, 92, 80, 87}},

new Student {FirstName = "Cesar", LastName = "Garcia", ID = 114,

Year = GradeLevel.FourthYear,

ExamScores = new List<int>{ 97, 89, 85, 82}},

new Student {FirstName = "Debra", LastName = "Garcia", ID = 115,

Year = GradeLevel.ThirdYear,

ExamScores = new List<int>{ 35, 72, 91, 70}},

new Student {FirstName = "Hugo", LastName = "Garcia", ID = 118,

Year = GradeLevel.SecondYear,

ExamScores = new List<int>{ 92, 90, 83, 78}},

new Student {FirstName = "Sven", LastName = "Mortensen", ID = 113,

Year = GradeLevel.FirstYear,

ExamScores = new List<int>{ 88, 94, 65, 91}},

new Student {FirstName = "Claire", LastName = "O'Donnell", ID = 112,

Year = GradeLevel.FourthYear,

ExamScores = new List<int>{ 75, 84, 91, 39}},

new Student {FirstName = "Svetlana", LastName = "Omelchenko", ID = 111,

Year = GradeLevel.SecondYear,

ExamScores = new List<int>{ 97, 92, 81, 60}},

new Student {FirstName = "Lance", LastName = "Tucker", ID = 119,

Year = GradeLevel.ThirdYear,

ExamScores = new List<int>{ 68, 79, 88, 92}},

new Student {FirstName = "Michael", LastName = "Tucker", ID = 122,

Year = GradeLevel.FirstYear,

ExamScores = new List<int>{ 94, 92, 91, 91}},

new Student {FirstName = "Eugene", LastName = "Zabokritski", ID = 121,

Year = GradeLevel.FourthYear,

ExamScores = new List<int>{ 96, 85, 91, 60}}

};

#endregion

//Helper method, used in GroupByRange.

protectedstaticintGetPercentile(Student s)

{

doubleavg = s.ExamScores.Average();

returnavg> 0 ? (int)avg / 10 : 0;

}

publicvoidQueryHighScores(int exam, int score)

{

varhighScores = from student in students

wherestudent.ExamScores[exam] > score

selectnew {Name = student.FirstName, Score = student.ExamScores[exam]};

foreach (var item inhighScores)

{

Console.WriteLine("{0,-15}{1}", item.Name, item.Score);

}

}

}

publicclass Program

{

publicstaticvoid Main()

{

StudentClasssc = newStudentClass();

sc.QueryHighScores(1, 90);

// Keep the console window open in debug mode.

Console.WriteLine("Press any key to exit");

Console.ReadKey();

}

}

В следующем примере показывается группировка элементов источника с помощью использования отдельного свойства элемента в качестве ключа группы. В данном случае в качестве ключа используется string, фамилия учащегося. Для ключа можно также использовать подстроку. При операции группирования используется компаратор проверки на равенство, используемый по умолчанию для данного типа.

Вставьте приведенный ниже метод в класс StudentClass. Измените оператор вызова в методе Main на sc.GroupBySingleProperty().

publicvoidGroupBySingleProperty()

{

Console.WriteLine("Group by a single property in an object:");

// Variable queryLastNames is an IEnumerable<IGrouping<string,

// DataClass.Student>>.

varqueryLastNames =

from student in students

group student bystudent.LastNameintonewGroup

orderbynewGroup.Key

selectnewGroup;

foreach (varnameGroupinqueryLastNames)

{

Console.WriteLine("Key: {0}", nameGroup.Key);

foreach (var student innameGroup)

{

Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);

}

}

}

/* Output:

Group by a single property in an object:

Key: Adams

Adams, Terry

Key: Fakhouri

Fakhouri, Fadi

Key: Feng

Feng, Hanying

Key: Garcia

Garcia, Cesar

Garcia, Debra

Garcia, Hugo

Key: Mortensen

Mortensen, Sven

Key: O'Donnell

O'Donnell, Claire

Key: Omelchenko

Omelchenko, Svetlana

Key: Tucker

Tucker, Lance

Tucker, Michael

Key: Zabokritski

Zabokritski, Eugene

*/

В следующем примере показывается группировка элементов источника, когда в качестве ключа группы используется не свойство объекта. В данном случае в качестве ключа используется первая буква фамилии учащегося.

Вставьте приведенный ниже метод в класс StudentClass. Измените оператор вызова в методе Main на sc.GroupBySubstring().

publicvoidGroupBySubstring()

{

Console.WriteLine("\r\nGroup by something other than a property of the object:");

varqueryFirstLetters =

from student in students

group student bystudent.LastName[0];

foreach (varstudentGroupinqueryFirstLetters)

{

Console.WriteLine("Key: {0}", studentGroup.Key);

// Nestedforeach is required to access group items.

foreach (var student instudentGroup)

{

Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);

}

}

}

/* Output:

Group by something other than a property of the object:

Key: A

Adams, Terry

Key: F

Fakhouri, Fadi

Feng, Hanying

Key: G

Garcia, Cesar

Garcia, Debra

Garcia, Hugo

Key: M

Mortensen, Sven

Key: O

O'Donnell, Claire

Omelchenko, Svetlana

Key: T

Tucker, Lance

Tucker, Michael

Key: Z

Zabokritski, Eugene

*/

В следующем примере показывается группировка элементов источника с помощью использования числового диапазона в качестве ключа группы. Затем запрос передает результаты в анонимный тип, содержащий только имя и фамилию, а также диапазон процентилей, к которому принадлежит студент. Использование анонимного типа объясняется тем, что для отображения результатов не требуется использовать полный объект Student. GetPercentile — это вспомогательная функция, которая вычисляет процент на основе средних результатов учащихся. Метод возвращает целое число в диапазоне от 0 до 10.

protected static int GetPercentile(Student s)

{doubleavg = s.ExamScores.Average();

returnavg> 0 ? (int)avg / 10 : 0;

}

Вставьте приведенный ниже метод в класс StudentClass. Измените оператор вызова в методе Main на sc.GroupByRange().

public void GroupByRange()

{

Console.WriteLine("\r\nGroup by numeric range and project into a new anonymous type:");

var queryNumericRange =

from student in students

let percentile = GetPercentile(student)

group new { student.FirstName, student.LastName } by percentile into percentGroup

orderby percentGroup.Key

select percentGroup;

// Nestedforeach required to iterate over groups and group items.

foreach (varstudentGroupinqueryNumericRange)

{

Console.WriteLine("Key: {0}", (studentGroup.Key * 10));

foreach (var item instudentGroup)

{

Console.WriteLine("\t{0}, {1}", item.LastName, item.FirstName);

} } }

/* Output:

Group by numeric range and project into a new anonymous type:

Key: 60

Garcia, Debra

Key: 70

O'Donnell, Claire

Key: 80

Adams, Terry

Feng, Hanying

Garcia, Cesar

Garcia, Hugo

Mortensen, Sven

Omelchenko, Svetlana

Tucker, Lance

Zabokritski, Eugene

Key: 90

Fakhouri, Fadi

Tucker, Michael

*/

В следующем примере показывается группировка элементов источника с помощью выражения логического сравнения. В этом примере логическое выражение проверяет значение среднего экзаменационного результата учащегося, превосходит ли оно 75 баллов. Как и в предыдущих примерах, результаты передаются в анонимный тип, поскольку весь элемент источника не требуется. Обратите внимание на то, что свойства в анонимном типе становятся свойствами в члене Key, а при выполнении запроса доступ к ним можно получить по имени.

Вставьте приведенный ниже метод в класс StudentClass. Измените оператор вызова в методе Main на sc.GroupByBoolean().

publicvoidGroupByBoolean()

{

Console.WriteLine("\r\nGroup by a Boolean into two groups with string keys");

Console.WriteLine("\"True\" and \"False\" and project into a new anonymous type:");

varqueryGroupByAverages = from student in students

groupnew { student.FirstName, student.LastName }

bystudent.ExamScores.Average() > 75 intostudentGroup

selectstudentGroup;

foreach (varstudentGroupinqueryGroupByAverages)

{

Console.WriteLine("Key: {0}", studentGroup.Key);

foreach (var student instudentGroup)

Console.WriteLine("\t{0} {1}", student.FirstName, student.LastName);

}

}

/* Output:

Group by a Boolean into two groups with string keys

"True" and "False" and project into a new anonymous type:

Key: True

Terry Adams

FadiFakhouri

HanyingFeng

Cesar Garcia

Hugo Garcia

Sven Mortensen

Svetlana Omelchenko

Lance Tucker

Michael Tucker

Eugene Zabokritski

Key: False

DebraGarcia

ClaireO'Donnell

*/

В следующем примере показано, как использовать анонимный тип для инкапсуляции ключа, содержащего несколько значений. В данном случае в качестве первого значения ключа используется первая буква фамилии учащегося. Второе значение ключа является логическим и указывает, набрал ли учащийся более 85 баллов на первом экзамене. Группы можно сортировать по любому из свойств в данном ключе.

Вставьте приведенный ниже метод в класс StudentClass. Измените оператор вызова в методе Main на sc.GroupByCompositeKey().

C#

publicvoidGroupByCompositeKey()

{

varqueryHighScoreGroups =

from student in students

group student bynew { FirstLetter = student.LastName[0],

Score = student.ExamScores[0] > 85 } intostudentGroup

orderbystudentGroup.Key.FirstLetter

selectstudentGroup;

Console.WriteLine("\r\nGroup and order by a compound key:");

foreach (varscoreGroupinqueryHighScoreGroups)

{

string s = scoreGroup.Key.Score == true ? "more than" : "less than";

Console.WriteLine("Name starts with {0} who scored {1} 85", scoreGroup.Key.FirstLetter, s);

foreach (var item inscoreGroup)

{

Console.WriteLine("\t{0} {1}", item.FirstName, item.LastName);

}

}

}

/* Output:

Group and order by a compound key:

Name starts with A who scored more than 85

Terry Adams

Name starts with F who scored more than 85

FadiFakhouri

HanyingFeng

Name starts with G who scored more than 85

Cesar Garcia

Hugo Garcia

Name starts with G who scored less than 85

Debra Garcia

Name starts with M who scored more than 85

Sven Mortensen

Name starts with O who scored less than 85

Claire O'Donnell

Name starts with O who scored more than 85

Svetlana Omelchenko

Name starts with T who scored less than 85

Lance Tucker

Name starts with T who scored more than 85

Michael Tucker

Name starts with Z who scored more than 85

EugeneZabokritski

*/

В терминах реляционных баз данных внутреннее соединение возвращает результирующий набор, в котором каждый элемент первой коллекции появляется один раз для каждого соответствующего элемента второй коллекции. Если для элемента первой коллекции нет соответствующего элемента, он не появляется в результирующем наборе.Метод Join, вызываемый предложением join в C#, реализует внутреннее соединение.

В этом разделе демонстрируется выполнение следующих четырех типов внутреннего соединения.

  • Простое внутреннее соединение, которое сопоставляет элементы из двух источников данных на основании простого ключа.

  • Внутреннее соединение, которое сопоставляет элементы из двух источников данных на основании составного ключа. Составной ключ (ключ, который содержит несколько значений) позволяет сопоставлять элементы на основании нескольких свойств.

  • При выполнении множественного соединения последовательные операции соединения применяются друг к другу.

  • Внутреннее соединение, которое реализуется с помощью группового соединения.

Пример