مقدمه

در درس قبل، دیدیم که چگونه می‌توان با استفاده از LINQ به راحتی روی آرایه‌ها کوئری زد. قدرت واقعی LINQ در این است که این تکنولوژی محدود به آرایه‌ها نیست. از آنجایی که تمام عملگرهای LINQ به عنوان متدهای بسطی بر روی اینترفیس IEnumerable<T> تعریف شده‌اند، ما می‌توانیم از دقیقاً همان سینتکس و همان عملگرها برای کوئری زدن روی هر نوع کالکشن جنریکی که این اینترفیس را پیاده‌سازی می‌کند، استفاده کنیم. این شامل List<T>، Dictionary<TKey, TValue> و دیگر کالکشن‌های استاندارد .NET می‌شود. در این درس، با اعمال کوئری‌های LINQ بر روی یک کالکشن List<T> از اشیاء سفارشی، این مفهوم را به صورت عملی بررسی می‌کنیم.

آماده‌سازی یک مثال پیچیده‌تر

برای نمایش بهتر قدرت LINQ، به جای کار با انواع داده‌ی ساده مانند int یا string، یک کلاس Employee تعریف کرده و لیستی از کارمندان را به عنوان منبع داده‌ی خود ایجاد می‌کنیم.

Copy Icon Program.cs
public record Employee(int Id, string Name, string Department, decimal Salary);

// Our data source: a List of Employee objects.
var employees = new List<Employee>
{
    new(1, "Sara", "HR", 55000m),
    new(2, "Ali", "Engineering", 80000m),
    new(3, "Mina", "Sales", 65000m),
    new(4, "Reza", "Engineering", 95000m),
    new(5, "Babak", "Sales", 70000m)
};

اعمال عملگرهای رایج LINQ

اکنون می‌توانیم همان عملگرهایی را که روی آرایه‌ها استفاده کردیم، بر روی لیست کارمندان خود نیز به کار ببریم.

فیلتر کردن با where

فرض کنید می‌خواهیم تمام کارمندانی را که در دپارتمان "Engineering" هستند، پیدا کنیم.

Copy Icon Program.cs
// Find all employees in the "Engineering" department.
var engineers = employees.Where(emp => emp.Department == "Engineering");

foreach (var engineer in engineers)
{
    Console.WriteLine(engineer.Name);
}

در این مثال، عبارت لامبدای emp => emp.Department == "Engineering" شرط فیلتر ماست. این عبارت برای هر کارمند (emp) در لیست اجرا شده و اگر پراپرتی Department او برابر با "Engineering" باشد، مقدار true را برمی‌گرداند و آن کارمند در نتیجه‌ی نهایی قرار می‌گیرد.

مرتب‌سازی با orderby

حالا بیایید کارمندان را بر اساس حقوقشان، از بیشترین به کمترین، مرتب کنیم.

Copy Icon Program.cs
// Using Query Syntax to sort employees by salary, descending.
var sortedBySalary = from emp in employees
                         orderby emp.Salary descending
                         select emp;
                         
foreach (var emp in sortedBySalary)
{
    Console.WriteLine($"{emp.Name}: {emp.Salary:C0}");
}

زنجیره‌سازی عملگرها بر روی کالکشن‌ها

قدرت LINQ در ترکیب و زنجیره‌سازی عملگرها برای ساخت کوئری‌های پیچیده است. بیایید یک کوئری بنویسیم که نام کارمندان دپارتمان "Sales" را که حقوقی بیش از ۶۸۰۰۰ دارند، پیدا کرده و به ترتیب حروف الفبا برگرداند.

Copy Icon Program.cs
// Chaining multiple operators using Method Syntax.
var highEarningSales = employees
    .Where(e => e.Department == "Sales" && e.Salary > 68000)
    .OrderBy(e => e.Name)
    .Select(e => e.Name); // Projecting to just the name.

Console.WriteLine("\nHigh-earning sales employees:");
foreach (var name in highEarningSales)
{
    Console.WriteLine(name); // Output: Babak
}

این کوئری زنجیره‌ای بسیار خوانا و گویاست. ما به ترتیب: ۱) فیلتر می‌کنیم، ۲) مرتب می‌کنیم، و ۳) داده‌ی نهایی را به شکل دلخواه (فقط نام) پرتاب می‌کنیم.

عملگرهای اجرای فوری

همانطور که در درس قبل اشاره شد، بیشتر عملگرهای LINQ دارای اجرای تأخیری هستند. اما برخی از عملگرها برای اینکه بتوانند یک مقدار واحد را برگردانند، کوئری را بلافاصله اجرا می‌کنند. آشنایی با این عملگرها بسیار مهم است.

  • Count(): تعداد عناصر یک دنباله (یا عناصری که شرطی را برآورده می‌کنند) را برمی‌گرداند.
  • First() / FirstOrDefault(): اولین عنصر دنباله را برمی‌گرداند. `First` در صورت خالی بودن دنباله، استثناء پرتاب می‌کند، در حالی که `FirstOrDefault` مقدار پیش‌فرض نوع را برمی‌گرداند.
  • Sum(), Average(), Max(), Min(): عملیات تجمعی را بر روی دنباله‌ای از مقادیر عددی انجام می‌دهند.
// This query executes immediately and returns an integer.
int engineeringCount = employees.Count(e => e.Department == "Engineering");
Console.WriteLine($"\nNumber of engineers: {engineeringCount}"); // Output: 2

// This query also executes immediately to find the highest salary.
decimal maxSalary = employees.Max(e => e.Salary);
Console.WriteLine($"Highest salary: {maxSalary:C0}"); // Output: $95,000