В этой статье мы продемонстрируем многотабличный запрос в базовом синтаксисе пакета расширения LINQ с запросом соединения соединения в качестве основного содержимого. В настоящее время LINQ поддерживает два синтаксиса. Сначала я буду использовать хорошо известный оператор SQL для выражения каждого случая, а затем использовать два синтаксиса LINQ C# для их реализации соответственно. Синтаксис LINQ неизбежно покажется вам незнакомым, когда вы впервые столкнетесь с ним. Лучший способ изучить его — это чаще использовать его в проектах.
Перед обучением нам необходимо выполнить некоторую подготовительную работу. Нам нужно создать объект User и коллекцию, содержащую объекты User, в качестве источника данных для последующего запроса и вывода.
Класс С#:
class User
{
public int id { get; set; }
public string name { get; set; }
public bool gender { get; set; }//male: true; female: fasle
public int age { get; set; }
public string occupation { get; set; } //Профессия
}
List<User> list = new List<User>()
{
new User { id = 1, name = "Zhang Long", age = 38, gender = true, occupation = "Teacher"},
new User { id = 2, name = "Zhang Jin", age = 18, gender = false, occupation = "Student"},
new User { id = 3, name = "Zhang Shuai", age = 38, gender = false, occupation = "Teacher"},
new User { id = 4, name = "Liu Guangzhi", age = 38, gender = false, occupation = "Doctor"},
new User { id = 5, name = "Liu Ziming", age = 38, gender = true, occupation = "Doctor"},
new User { id = 6, name = "Liu Shuai", age = 29, gender = false, occupation = "Doctor"},
new User { id = 7, name = "Liu Jin", age = 21, gender = true, occupation = "Builder"},
new User { id = 8, name = "Jiang Long", age = 38, gender = true, occupation = "Builder"},
new User { id = 9, name = "Hu Ziming", age = 21, gender = true, occupation = "Student"},
new User { id = 10, name = "Hu Jin", age = 21, gender = false, occupation = "Student"}
};
Источник данных 1:
Источник данных 2:
Общие запросы соединения в SQL включают:
Linq имеет только функцию Join.
Запрос соединения соединения в Linq изменяет направление левого и правого соединений, меняя порядок связанной таблицы и связанной таблицы. Регулируя положение функций условной фильтрации, таких как «Где» и «Вкл», логика изменяется для достижения большего. сложные функции, такие как внутренние соединения и полные соединения.
Аналогично, метод соединения Linq также имеет два способа записи:
/* Способ написания C# 1 <LinqМетод письма>*/
IEnumerable<Salary> SalaryList =
from u in list
join s in salaryList
on u.id equals s.user_id
select s;
/*Способ написания C# 2 <LambdaМетод письма>*/
IEnumerable<Salary> SalaryList = list
.Join(
inner: salaryList, /*inner: Можно опустить*/
outerKeySelector: u => u.id, /*outerKeySelector: Можно опустить*/
innerKeySelector: s => s.user_id, /*innerKeySelector: Можно опустить*/
resultSelector: (u, s) => s /*resultSelector: Можно опустить*/
);
Как показано в двух таблицах выше, user_id таблицы данных 2 является внешним ключом таблицы данных 1, соответствующим идентификатору таблицы данных 1. Различные атрибуты двух таблиц могут сопоставляться один за другим через пользователя через связанные запросы.
Давайте рассмотрим пример, используя корреляционный запрос для запроса информации о пользователе в таблице 1 и соответствующей информации о зарплате в таблице 2:
/* Выражение в SQL: Запросить имя, возраст, профессию, пол, статус занятости и зарплату всех пользователей*/
SELECT u.id, u.name, u.age, u.occupation, u.gender, s.active, s.salary
FROM
User AS u
LEFT JOIN
Salary AS s
ON u.id = s.user_id;
/* Способ написания C# 1*/
IEnumerable<UserSalary> UserSalaryList =
from u in list
join s in salaryList on u.id equals s.user_id
select new UserSalary
{
id = u.id,
name = u.name,
age = u.age,
occupation = u.occupation,
gender = u.gender,
active = s.active,
salary = s.salary
};
/*Способ написания C# 2*/
IEnumerable<UserSalary> UserSalaryList = list
.Join(salaryList, u => u.id, s => s.user_id, (u, s) => new UserSalary
{
id = u.id,
name = u.name,
age = u.age,
occupation = u.occupation,
gender = u.gender,
active = s.active,
salary = s.salary
});
/* траверс выход */
foreach (UserSalary user in UserSalaryList)
{
Console.WriteLine(PrintUserSalaryObject(user));
}
/* выходрезультат */
{id = 1, name = Zhang Long, age = 38, gender = True, occupation = Teacher, active = True, salary = 7800}
{id = 2, name = Zhang Jin, age = 18, gender = False, occupation = Student, active = True, salary = 1500}
{id = 3, name = Zhang Shuai, age = 38, gender = False, occupation = Teacher, active = False, salary = 8800}
{id = 4, name = Liu Guangzhi, age = 38, gender = False, occupation = Doctor, active = True, salary = 12800}
{id = 5, name = Liu Ziming, age = 38, gender = True, occupation = Doctor, active = True, salary = 13600}
{id = 6, name = Liu Shuai, age = 29, gender = False, occupation = Doctor, active = False, salary = 29000}
{id = 7, name = Liu Jin, age = 21, gender = True, occupation = Builder, active = True, salary = 7000}
{id = 8, name = Jiang Long, age = 38, gender = True, occupation = Builder, active = False, salary = 8500}
{id = 9, name = Hu Ziming, age = 21, gender = True, occupation = Student, active = True, salary = 2100}
{id = 10, name = Hu Jin, age = 21, gender = False, occupation = Student, active = True, salary = 1300}
/* Выражение в SQL: Используйте два атрибута name и id, чтобы связать таблицу пользователей и таблицу зарплат.
Запросить информацию о зарплате всех пользователей мужского пола, которые работают*/
/* Метод записи SQL 1*/
SELECT * FROM User AS u
RIGHT JOIN Salary AS s
ON u.id = s.user_id AND u.name = s.name
AND u.gender = true AND s.active = true;
/* Метод записи SQL 2*/
/*Поместите условия фильтрации в Where позади JoinOn, чтобы избежать левых и правых соединений.
Проблема, вызванная полупустыми значениями, эквивалентна сначала корреляции, а затем фильтрации*/
SELECT * FROM User AS u
RIGHT JOIN Salary AS s
ON u.id = s.user_id AND u.name = s.name
WHERE u.gender = true AND s.active = true;
/* Способ написания C# 1*/
/*Такой способ записи не рекомендуется, поскольку результирующий набор будет содержать значение null.
После рекомендации ассоциации получите данные, а затем отфильтруйте их.
То есть пропишите условия фильтрации в обработке набора результатов после Join*/
IEnumerable<Salary> JointList = (
from r1 in list
where r1.gender
join r2 in (from r3 in salaryList
where r3.active select r3)
on new
{
ID = r1.id,
r1.name
}
equals new
{
ID = r2.user_id,
r2.name
}
into cls
from c in cls.DefaultIfEmpty()
select c
).ToList();
/*Способ написания C# 2*/
IEnumerable<Salary> JointList = (
from r1 in list
where r1.gender
join r2 in salaryList
on new
{
ID = r1.id,
r1.name
}
equals new
{
ID = r2.user_id,
r2.name
}
into cls
from c in cls.DefaultIfEmpty()
where c.active
select c
).ToList();
/*Метод написания C# 3*/
IEnumerable<Salary> JointList = (
from r1 in list
from r2 in salaryList
where
r2.active &&
r1.id == r2.user_id &&
r1.name == r2.name &&
r1.gender
select r2
).ToList();
/*Метод записи C# 4 <LambdaМетод письма>*/
IEnumerable<Salary> JointList =
list.Where(u => u.gender)
.Join(
salaryList.Where(s => s.active),
u => new { ID = u.id, u.name },
s => new { ID = s.user_id, s.name },
(u, s) => s
);
/*Метод написания C# 5 <LambdaМетод письма>*/
/*Поместите фильтрацию коллекции Salary в метод 4 после всего запроса на соединение.
Поскольку пол является уникальным атрибутом Пользователя, фильтрация пола невозможна.
Поместите его после набора результатов, объектом содержимого которого является Salary*/.
IEnumerable<Salary> JointList =
list.Where(u => u.gender)
.Join(
salaryList,
u => new { ID = u.id, u.name },
s => new { ID = s.user_id, s.name },
(u, s) => s
).Where(s => s.active);
/* траверс выход */
foreach (Salary salary in JointList)
{
if(salary != null)
Console.WriteLine(PrintUserSalaryObject(salary));
}
/* выходрезультат */
{id = 1, name = Zhang Long, occupation = Teacher, active = True, salary = 7800}
{id = 5, name = Liu Ziming, occupation = Doctor, active = True, salary = 13600}
{id = 7, name = Liu Jin, occupation = Builder, active = True, salary = 7000}
{id = 9, name = Hu Ziming, occupation = Student, active = True, salary = 2100}
/*Синтаксис соединения для пользовательских условий Linq*/
public static System.Collections.Generic.IEnumerable<TResult> Join<TOuter,TInner,TKey,TResult> (
this System.Collections.Generic.IEnumerable<TOuter> outer,
System.Collections.Generic.IEnumerable<TInner> inner,
Func<TOuter,TKey> outerKeySelector, //Результат Tkey должен содержать следующие параметры, необходимые компаратору
Func<TInner,TKey> innerKeySelector, //Результат Tkey должен содержать следующие параметры, необходимые компаратору
Func<TOuter,TInner,TResult> resultSelector,
System.Collections.Generic.IEqualityComparer<TKey> comparer);
/* Обратите особое внимание на то, что входящий параметр компаратора равенства здесь имеет тип TKey.
Должен соответствовать типу TKey externalKeySelector и InnerKeySelector*/.
Например:
На основе того же имени и профессии, связанной с информацией о пользователе и зарплате, запросите информацию о зарплате мужчины и работающего человека.
/*Создаем новый компаратор равенства CompareUser*/
/*Возвращает true, если свойства имени и профессии двух объектов User равны, в противном случае — false*/
class CompareUser : IEqualityComparer<User>
{
public bool Equals(User x, User y)
{
if (x.name == y.name && x.occupation.ToLower() == y.occupation.ToLower())
return true;
return false;
}
public int GetHashCode(User obj)
{
return (obj.name + obj.occupation).Length;
}
}
/*Реализация выражения Linq находит требования равенства, которые соответствуют компаратору равенства CompareUser и удовлетворяют нескольким условиям*/
IEnumerable<Salary> JointList = list.Where(u => u.gender)
.Join<User, Salary, User, Salary>(
inner: salaryList,
/*Поскольку компаратор использует два атрибута: имя и род занятий, селектор здесь должен содержать эти два полезных значения атрибута*/
outerKeySelector: u => new User{ name = u.name, occupation = u.occupation},
innerKeySelector: s => new User{ name = s.name, occupation = s.occupation },
resultSelector: (u, s) => s,
comparer: new CompareUser()
).Where(s => s.active);
/* траверс выход */
foreach (Salary salary in JointList)
{
if(salary != null)
Console.WriteLine(PrintUserSalaryObject(salary));
}
/* выходрезультат */
{id = 1, name = Zhang Long, occupation = Teacher, active = True, salary = 7800}
{id = 5, name = Liu Ziming, occupation = Doctor, active = True, salary = 13600}
{id = 7, name = Liu Jin, occupation = Builder, active = True, salary = 7000}
{id = 9, name = Hu Ziming, occupation = Student, active = True, salary = 2100}
Разница между GroupJoin и Join заключается в том, что набор результатов группируется по GroupBy. Это самый сложный пример использования нескольких условий и настройки, поскольку он представляет собой комбинацию синтаксиса Join и синтаксиса GroupBy. Его нетрудно понять и нетрудно понять. требуют большого описания.
/* Синтаксис группового соединения */
IEnumerable<ListMultiGroupResult2> JointList = list.Where(u => u.gender)
.GroupJoin(
inner: salaryList.Where(s => s.active),
outerKeySelector: u => new User { name = u.name, occupation = u.occupation },
innerKeySelector: s => new User { name = s.name, occupation = s.occupation },
resultSelector: (u, s) => new ListMultiGroupResult2 {
Occupation = u.occupation,
Name = u.name,
SalaryList = s.ToList() },
comparer: new CompareUser()
);
/*Настраиваемый компаратор равенства CompareUser*/
class CompareUser : IEqualityComparer<User>
{
public bool Equals(User x, User y)
{
if (x.name == y.name && x.occupation.ToLower() == y.occupation.ToLower())
return true;
return false;
}
public int GetHashCode(User obj)
{
return (obj.name + obj.occupation).Length;
}
}
/* траверс выход */
foreach (ListMultiGroupResult2 s in JointList)
{
Console.WriteLine(s.Occupation + "/" + s.Name);
foreach (Salary salary in s.SalaryList)
{
Console.WriteLine(PrintSalaryObject(salary));
}
}
/* выходрезультат */
Teacher/Zhang Long
{id = 1, name = Zhang Long, occupation = Teacher, active = True, salary = 7800}
Doctor/Liu Ziming
{id = 5, name = Liu Ziming, occupation = Doctor, active = True, salary = 13600}
Builder/Liu Jin
{id = 7, name = Liu Jin, occupation = Builder, active = True, salary = 7000}
Builder/Jiang Long
Student/Hu Ziming
{id = 9, name = Hu Ziming, occupation = Student, active = True, salary = 2100}
LINQ (Language Integrated Query) предоставляет мощную функцию запроса соединения соединения, которая особенно важна в запросах к нескольким таблицам. В операциях с базой данных или другими источниками данных операции соединения позволяют объединять данные из нескольких таблиц, что значительно повышает гибкость и эффективность обработки данных. Используя синтаксис C# или VB.NET, запрос LINQ Join не только упрощает сложную логику запроса, но также улучшает читаемость и удобство обслуживания кода.
Сценарии использования многотабличных запросов:
Запрос LINQ Join предоставляет очень мощный и гибкий набор инструментов для обработки сложных ассоциаций и интеграции данных из нескольких источников. Правильное использование этих инструментов позволяет не только оптимизировать процесс обработки данных, но и значительно повысить эффективность и качество запросов данных. По мере увеличения объема данных и усложнения требований к запросам запросы LINQ Join показали свою незаменимую ценность в ежедневных операциях с данными и их анализе.