Lesson ১২৮ মিনিট পড়া

ল্যাঙ্গুয়েজ ইন্টিগ্রেটেড কোয়েরি বা লিংক (LINQ)

একেবারে সি শার্পের (C#) ভেতর থেকেই যেকোনো সার্চ ইঞ্জিনের (search engine) মতো আপনার ডেটা খুঁজুন

লিংক বা LINQ কী? (What Is LINQ?)

LINQ এর পূর্ণরূপ হলো Language Integrated Query (ল্যাঙ্গুয়েজ ইন্টিগ্রেটেড কোয়েরি)। এর মাধ্যমে আপনি সি শার্পের (C#) যেকোনো কালেকশনকে (collection) খুব সুন্দর এবং সহজে পড়া যায় এমন সিনট্যাক্সে (syntax) খুঁজতে (search), ফিল্টার (filter) করতে, সাজাতে (sort) এবং রূপান্তর (transform) করতে পারবেন — যা দেখতে অনেকটা এসকিউএল বা SQL ডেটাবেস কোয়েরির (database queries) মতোই।

ধরা যাক, আপনার কাছে ১,০০০ জন ছাত্র-ছাত্রীর একটি তালিকা (list) রয়েছে। LINQ ছাড়া এই তালিকাটি যাচাই বাছাই করার জন্য আপনাকে অনেকগুলো লুপ (loops), if-স্টেটমেন্টস (if-statements) এবং অস্থায়ী ভেরিয়েবল (temporary variables) লিখতে হতো। কিন্তু LINQ ব্যবহার করে আপনি শুধু এক লাইনেই লিখতে পারবেন, "১৮ বছরের বেশি বয়সী সব ছাত্র-ছাত্রীর নাম তাদের জিপিএ (GPA) অনুযায়ী সাজিয়ে আমাকে দাও।" কাজ শেষ।

LINQ ব্যবহারের দুটি পদ্ধতি রয়েছে:

  • মেথড সিনট্যাক্স (Method syntax): list.Where(...).Select(...) — এটি সবচেয়ে বেশি ব্যবহৃত হয় এবং এতে ল্যামবডা অ্যারো (lambda arrows) ব্যবহৃত হয়।
  • কোয়েরি সিনট্যাক্স (Query syntax): from x in list where ... select ... — এটি দেখতে অনেকটা SQL এর মতো।

উভয় পদ্ধতিতেই একই ধরনের ফলাফল পাওয়া যায়। বেশিরভাগ সি শার্প ডেভেলপার মেথড সিনট্যাক্স ব্যবহার করতে বেশি পছন্দ করেন, তবে বড় বা জটিল জয়নগুলোর (complex joins) জন্য কোয়েরি সিনট্যাক্স বেশ ভালো কাজ করে।

LINQ-এর বেসিক ধারণা — Where, Select, OrderBy (LINQ Basics — Where, Select, OrderBy)

var students = new List<(string Name, int Age, double GPA)>
{
("Anika", 20, 3.8),
("Rafi", 17, 3.2),
("Sadia", 22, 3.9),
("Tariq", 19, 2.7),
("Esha", 21, 3.5)
};
// Method syntax — filter, sort, transform
var honorRoll = students
.Where(s => s.GPA >= 3.5) // filter: GPA 3.5+
.OrderByDescending(s => s.GPA) // sort: highest first
.Select(s => $"{s.Name} ({s.GPA})"); // transform: just name + GPA
Console.WriteLine("Honor Roll (method syntax):");
foreach (var entry in honorRoll)
Console.WriteLine($" {entry}");
// Query syntax — same result, SQL-like style
var honorRoll2 = from s in students
where s.GPA >= 3.5
orderby s.GPA descending
select $"{s.Name} ({s.GPA})";
Console.WriteLine("\nHonor Roll (query syntax):");
foreach (var entry in honorRoll2)
Console.WriteLine($" {entry}");
Output
Honor Roll (method syntax):
  Sadia (3.9)
  Anika (3.8)
  Esha (3.5)

Honor Roll (query syntax):
  Sadia (3.9)
  Anika (3.8)
  Esha (3.5)

শক্তিশালী কিছু LINQ মেথডস (Powerful LINQ Methods)

LINQ-এ ডজনখানেক মেথড (methods) রয়েছে। নিচে এমন কিছু মেথডের কথা বলা হলো যেগুলো আপনার সব সময়ই কাজে লাগবে:

  • .Where() — কোনো শর্ত বা কন্ডিশন (condition) পূরণ করে এমন আইটেমগুলোকে ফিল্টার করে
  • .Select() — প্রতিটা আইটেমকে নিয়ে অন্য কিছুতে রূপান্তর করে
  • .OrderBy() / .OrderByDescending() — ক্রমানুসারে সাজায়
  • .GroupBy() — একটি কি (key) বা চাবির ওপর ভিত্তি করে আইটেমগুলোকে গ্রুপ (group) বা দলবদ্ধ করে
  • .First() / .FirstOrDefault() — শর্ত পূরণ করে এমন প্রথম আইটেমটিকে খুঁজে বের করে
  • .Any() — শর্তের সাথে বা বর্ণনার সাথে মেলে এমন অন্তত একটি (AT LEAST ONE) আইটেম কি আছে? (এটি bool রিটার্ন করে)
  • .All() — সব আইটেমই কি (ALL items) শর্ত পূরণ করে? (এটি bool রিটার্ন করে)
  • .Count() — কতগুলো আইটেম শর্ত পূরণ করতে পেরেছে?
  • .Sum() / .Average() / .Min() / .Max() — কালেকশনের (collections) ওপর গাণিতিক হিসাব-নিকাশ করে
  • .Distinct() — পুনরাবৃত্তি বা ডুপ্লিকেট (duplicates) সরিয়ে দেয়
  • .Take(n) / .Skip(n) — পেজিনেশন (pagination) তৈরি করে

Any, All, First, Count, GroupBy

var products = new List<(string Name, string Category, decimal Price)>
{
("Laptop", "Electronics", 999.99m),
("Mouse", "Electronics", 29.99m),
("Desk", "Furniture", 249.99m),
("Chair", "Furniture", 399.99m),
("Headphones", "Electronics", 79.99m),
("Lamp", "Furniture", 49.99m)
};
// Any — is there anything expensive?
bool hasExpensive = products.Any(p => p.Price > 500);
Console.WriteLine($"Has expensive items: {hasExpensive}");
// All — is everything under $100?
bool allCheap = products.All(p => p.Price < 100);
Console.WriteLine($"All items under $100: {allCheap}");
// First — get the cheapest item
var cheapest = products.OrderBy(p => p.Price).First();
Console.WriteLine($"Cheapest: {cheapest.Name} at {cheapest.Price:C}");
// Count & Sum
int electronicCount = products.Count(p => p.Category == "Electronics");
decimal totalValue = products.Sum(p => p.Price);
Console.WriteLine($"Electronics: {electronicCount} items");
Console.WriteLine($"Total value: {totalValue:C}");
// GroupBy — group products by category
var groups = products.GroupBy(p => p.Category);
Console.WriteLine("\nBy category:");
foreach (var group in groups)
{
Console.WriteLine($" {group.Key}: {group.Count()} items, avg {group.Average(p => p.Price):C}");
}
Output
Has expensive items: True
All items under $100: False
Cheapest: Mouse at $29.99
Electronics: 3 items
Total value: $1,809.94

By category:
  Electronics: 3 items, avg $369.99
  Furniture: 3 items, avg $233.32

চেইনিং, Take/Skip এবং Distinct (Chaining, Take/Skip, and Distinct)

var numbers = Enumerable.Range(1, 20).ToList(); // [1, 2, 3, ... 20]
// Chain multiple operations
var result = numbers
.Where(n => n % 2 == 0) // evens: 2, 4, 6, 8...
.Select(n => n * n) // squared: 4, 16, 36, 64...
.Where(n => n > 20) // filter: 36, 64, 100...
.Take(3); // first 3 only
Console.WriteLine($"Chained: [{string.Join(", ", result)}]");
// Pagination with Skip and Take
var page1 = numbers.Skip(0).Take(5);
var page2 = numbers.Skip(5).Take(5);
var page3 = numbers.Skip(10).Take(5);
Console.WriteLine($"Page 1: [{string.Join(", ", page1)}]");
Console.WriteLine($"Page 2: [{string.Join(", ", page2)}]");
Console.WriteLine($"Page 3: [{string.Join(", ", page3)}]");
// Distinct — remove duplicates
var tags = new[] { "csharp", "dotnet", "csharp", "linq", "dotnet", "linq" };
var unique = tags.Distinct().OrderBy(t => t);
Console.WriteLine($"Unique tags: [{string.Join(", ", unique)}]");
// FirstOrDefault — safe first (returns default if empty)
int? firstBig = numbers.Where(n => n > 100).FirstOrDefault();
Console.WriteLine($"First > 100: {firstBig}"); // 0 (default for int)
Output
Chained: [36, 64, 100]
Page 1: [1, 2, 3, 4, 5]
Page 2: [6, 7, 8, 9, 10]
Page 3: [11, 12, 13, 14, 15]
Unique tags: [csharp, dotnet, linq]
First > 100: 0
Note: ⚡ LINQ খুবই অলস (lazy)! .Where() এবং .Select() এর মতো মেথডগুলো আসলে ততক্ষণ পর্যন্ত রান করে না বা কাজ শুরু করে না, যতক্ষণ না আপনি সেগুলোর ওপর দিয়ে একবার করে হেঁটে যান বা ইটারেট (iterate, যেমন foreach লুপ) করেন অথবা জোর করে ইভ্যালুয়েট বা চেক করান (যেমন .ToList(), .Count(), .First())। এর মানে হলো LINQ কেবল তখনই কাজ করে, যখন এর খুব বেশি প্রয়োজন হয় — উদাহরণস্বরূপ আপনি যদি Take(3) লেখেন, তবে এটি এক মিলিয়ন আইটেমের একটি লিস্ট থেকে প্রথম ৩টি আইটেম খোঁজার পর নিজেই থেমে যাবে।
চ্যালেঞ্জ

ছোট কুইজ

.First() এবং .FirstOrDefault() এর মধ্যে পার্থক্যটি কী?
Collections & GenericsException Handling