آموزش LINQ بخش پنجم
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: پنج دقیقه

در ادامه سری آموزشی LINQ، موارد دیگری از عبارت‌های پرس و جو را بررسی می‌کنیم:
 • عبارت group
 • عبارت orderby
 • کلمات کلیدی ascending و descending
 • کلمه کلیدی by


بررسی عبارت group و کلمه‌ی کلیدی by

عبارت group یک توالی یک بعدی (flat sequence) یا ساده را از ورودی دریافت و یک توالی گروه بندی شده را تولید می‌کند.
مثال: در بخش دوم این سری آموزشی، کلاسی به نام Ingredient تعریف کردیم که مواد غذایی و کالری آنها را نگهداری می‌کرد. در این مثال قصد داریم مواد غذایی دریافت شده از توالی ورودی را بر اساس کالری آنها، به گروه‌هایی تقسیم کنیم، بطوریکه مواد غذایی با کالری‌های یکسان در یک گروه قرار بگیرند:
Ingredient[] ingredients =
{
   new Ingredient{Name = "Sugar", Calories=500},
   new Ingredient{Name = "Lard", Calories=500},
   new Ingredient{Name = "Butter", Calories=500},
   new Ingredient{Name = "Egg", Calories=100},
   new Ingredient{Name = "Milk", Calories=100},
   new Ingredient{Name = "Flour", Calories=50},
   new Ingredient{Name = "Oats", Calories=50}
};

IEnumerable<IGrouping<int, Ingredient>> query =
from i in ingredients
group i by i.Calories;

foreach (IGrouping<int, Ingredient> group in query)
{
   Console.WriteLine($"Ingredients with {group.Key} calories");
   foreach (Ingredient ingredient in group)
   {
      Console.WriteLine($"- { ingredient.Name}");
   }
}
در این مثال، از نوع جنریکی به نام IGrouping استفاده شده‌است که اولین پارامتر آن کلید گروه است (در این مثال کالری مواد غذایی) و دومین پارامتر آن، نوع لیست را مشخص می‌کند که در اینجا کلاس Ingredient است. اعضای لیست تولید شده، کالری یکسانی دارند. دلیل استفاده‌ی از نوع‌ها بصورت صریح و آشکار (Explicit) خوانایی بیشتر برای تشخیص خروجی‌های تولید شده‌ی در کد‌ها می‌باشد. ولی راه بهتر، جایگزین کردن کد <<IEnumerable<IGrouping<int, Ingredient با کلمه‌ی کلیدی var  می‌باشد.
خروجی مثال بالا:
 Ingredients with 500 calories
- Sugar
- Lard
- Butter
Ingredients with 100 calories
- Egg
- Milk
Ingredients with 50 calories
- Flour
- Oats

برای آشنایی بیشتر با اینترفیس IGrouping اینجا را مطالعه کنید.


عبارت orderby و کلمات کلیدی ascending  و descending 

عبارت orderby برای تولید یک توالی مرتب شده‌ی بصورت صعودی (ascending)  و یا نزولی (descending) مورد استفاده قرار می‌گیرد.
مثال: توالی مثال قبل را در نظر بگیرد:
IOrderedEnumerable<Ingredient> sortedByNameQuery =
from i in ingredients
orderby i.Name
select i;

foreach (var ingredient in sortedByNameQuery)
{
   Console.WriteLine(ingredient.Name);
}
خروجی مثال بالا:
Butter
Egg
Flour
Lard
Milk
Oats
Sugar
علملیات مرتب سازی بصورت پیش فرض بصورت صعودی می‌باشد (Ascending). برای تغییر این حالت می‌توانید از کلمه‌ی کلیدی descending استفاده کنید:
IOrderedEnumerable<Ingredient> sortedByNameQuery =
from i in ingredients
orderby i.Name descending
select i;
با اجرای مجدد برنامه، خروجی زیر را مشاهده خواهید کرد:
 Sugar
Oats
Milk
Lard
Flour
Egg
Butter
عبارت orderby  را می‌توان با عبارت groupby ترکیب کرد و نتیجه‌ی حاصل از مثال اول این مطلب را بصورت مرتب شده بر اساس کالری مواد غذایی مشاهده کرد.
مثال:
 Ingredient[] ingredients =
{
   new Ingredient{Name = "Sugar", Calories=500},
   new Ingredient{Name = "Lard", Calories=500},
   new Ingredient{Name = "Butter", Calories=500},
   new Ingredient{Name = "Egg", Calories=100},
   new Ingredient{Name = "Milk", Calories=100},
   new Ingredient{Name = "Flour", Calories=50},
   new Ingredient{Name = "Oats", Calories=50}
};

IEnumerable<IGrouping<int, Ingredient>> query =
from i in ingredients
group i by i.Calories
into calorieGroup
orderby calorieGroup.Key
select calorieGroup;

foreach (IGrouping<int, Ingredient> group in query)
{
   Console.WriteLine($"Ingredients with {group.Key} calories");
   foreach (Ingredient ingredient in group)
   {
     Console.WriteLine($"- { ingredient.Name}");
   }
}
خروجی مثال بالا :
 Ingredients with 50 calories
- Flour
- Oats
Ingredients with 100 calories
- Egg
- Milk
Ingredients with 500 calories
- Sugar
- Lard
- Butter
همانطور که مشاهده می‌کنید بر عکس حالت قبلی که خروجی گروه‌ها بصورت نزولی بود، اینجا خروجی بر اساس کالری غذا و بصورت صعودی، نمایش داده شده است.


در این سری آموزشی با دو روش نوشتن پرس و جو، در LINQ آشنا شدیم:
 1- Fluent Style (استفاده از متدهای الحاقی برای انجام عملیات‌های مختلف بر روی توالی)
 2- Expression Style (استفاده از کلمات کلیدی (key word) برای انجام عملیات‌های مختلف بر روی توالی)

 هر یک از روش‌های فوق مزایایی دارند که با توجه به شرایطی که با آن روبرو هستیم از آنها استفاده می‌کنیم:
 • تعداد عملگر‌ها : در صورتی که پرس و جو نیاز به یک عملگر بر روی توالی داشته باشد می‌توان از روش اول استفاده کرد. به این خاطر که نسبت به روش دوم تعداد دستورات کمتری مورد استفاده قرار خواهد گرفت.
مثال :
 var q1 = ingredients.Where(x => x.Calories > 100);
var q2 = from i in ingredients
 where i.Calories >
100 select i;
در مثال فوق نتیجه‌ی اجرای دو پرس و جو، موادی است که کالری آنها بیشتر از 100 می‌باشد. همانطور که مشاهده می‌کنید روش اول مختصرتر از روش دوم است. این مورد در زمانی است که تنها یک عملیات بر روی توالی اجرا شود.
 • پرس و جویی که قرار است نوشته شود، از نظر عملیات بر روی توالی ساده باشد: در این حالت روش استفاده شده تفاوتی نمی‌کند و بستگی به روش برگزیده و مورد علاقه‌ی برنامه نویس و یا تیم برنامه نویسی دارد. مثلا زمانیکه تنها عملگر‌های Where  و orderby بخواهند اجرا شود.
 • پرس و جوهایی که با متغیر‌های range مختلفی رو برو هستند: در این حالت استفاده از عبارت‌های پرس و جو راحت از روش عملگر‌های پرس و جو می‌باشد.

لیستی از عملگرهای جستجو که در روش عبارت‌های جستجو معادل آنها مهیا شده است :
 • GroupBy
 • GroupJoin
 • Join
 • OrderBy
 • OrderByDescending
 • Select
 • SelectMany
 • ThenBy
 • ThenByDescending
 • Where

در بسیاری از پرس و جو‌ها می‌توانیم هر دو روش را با هم ترکیب کنیم که نمونه‌ای از آن، در جلسه‌ی چهارم در زمان استفاده‌ی از متد DefaultIfEmpty استفاده شد. درمثال زیر استفاده از عملگر count، با استفاده از روش اول، به همراه عبارت پرس و جوی تولید شده‌ی با روش دوم، نمایش داده شده است:
 int mixedQuery =
(from i in ingredients
where i.Calories > 100
select i).Count();