عبارت from

عبارت from منبع داده‌ای که می‌خواهید داده‌ای از آن دریافت کنید را تعریف می‌کند. این عبارت مسئول بدست آوردن عناصر یک منبع داده است. این عبارت در ابتدای یک عبارت پرس وجو می‌آید، بنا براین کل عبارت پرس و جو پس از آن، نوع داده‌ای که بر روی آن کار می‌کنند را می‌داند. این کار باعث می‌شود Visual Studio از ویژگی Intellisense بعد از این عبارت بهره مند شود زیرا نوع تک تک اعضای منبع داده را می‌داند. در زیر شکل کلی این عبارت را مشاهده می‌کنید :

from dataType rangeVar in dataSource

این عبارت با کلمه کلیدی from شروع می‌شود که جز Contextual Keywords در سی شارپ است. سپس باید یک متغیر موقت که تک تک اعضای یک مجموعه به ترتیب در آن قرار می‌گیرند را تعریف کنیم. همچنین شما می‌توانید نوع متغیر موقت را صریحاً مشخص کنید که کاربرد خاص خود را دارد. بعد از اعلان متغیر موقت کلمه کلیدی in همراه با نام منبع داده‌ای که رابط IEnumerable یا IEnumerable را پیاده سازی می‌کند قرار می‌گیرد. چیزی که در یک پرس و جوی ساده from اتفاق می افتد این است که این عبارت به ترتیب اعضای مجموعه را در متغیر موقت قرار می‌دهد. شما می‌توانید عملیات بیشتری را روی این متغیر مانند بررسی کردن یک شرط خاص انجام دهید. در LINQ قابلیتی به نام deferred execution (اجرای با تعویق) وجود دارد. به این معنی است که LINQ اجرا نمی‌شود مگر زمانی که با استفاده از حلقه foreach از نتیجه پرس وجو درخواست داده‌ای را می‌کنید. در زیر مثالی از عبارت from را مشاهده می‌کنید :

from int number in numbers

عبارت from بالا به کامپیوتر دستور می‌دهد که تک تک اعداد داخل یک مجموعه عددی را بر گرداند (با این فرض که آن مجموعه یک آرایه یا شی ای است که رابطIEnumerable را پیاده سازی می‌کند). به نوع متغیر موقت در مثال بالا توجه کنید. در بیشتر مواقع، اگر از نوع داده‌های منبع داده مطمئن هستید می‌توانید از نوشتن این نوع صرف نظر کنید.

from number in numbers

کامپایلر می‌تواند به صورت خودکار و بسته به نوع داده‌های منبع داده، نوع متغیر موقت را تشخیص دهد. به عنوان مثال اگر مجموعه‌ای داریم که رابط IEnumerable را پیاده سازی کرده است، نوع متغیر موقت به طور خودکار Person در نظر گرفته می‌شود. اما اگر قصد پرس و جو در مجموعه‌ای از اشیاء مانند ArrayList را دارید بهتر آن است که نوع متغیر موقت که بیانگر نوع عناصر داخل مجموعه است را صریحاً مشخص نمایید. مثال زیر را در نظر بگیرید :

   1: using System;
   2: using System.Linq;
   3: using System.Collections;
   4: using System.Collections.Generic;
   5: 
   6: namespace FromClause
   7: {
   8:     public class Person
   9:     {
  10:         public int    PersonId  { get; set; }
  11:         public string FirstName { get; set; }
  12:         public string LastName  { get; set; }
  13:         public string Gender    { get; set; }
  14:         public int    Age       { get; set; }
  15: 
  16:         public Person(string f, string l)
  17:         {
  18:             this.FirstName = f;
  19:             this.LastName  = l;
  20:         }
  21:     } 
  22: 
  23:     class Program
  24:     {
  25:         static void Main(string[] args)
  26:         {
  27:             ArrayList persons = new ArrayList();
  28:             persons.Add(new Person("John", "Smith"));
  29:             persons.Add(new Person("Mark", "Chandler"));
  30:             persons.Add(new Person("Eric", "Watts"));
  31: 
  32:             var query = from p in persons
  33:                         select p;
  34:         }
  35:     }
  36: }

اگر مثال بالا را کامپایل کنید با خطای زیر مواجه می‌شوید :

Could not find an implementation of the query pattern for source type 
'System.Collections.ArrayList'.  'Select' not found.  Consider explicitly specifying the 
type of the range variable 'p'.

به این خاطر که ArrayList می‌تواند شامل اشیاء از نوع‌های مختلفی باشد. کامپایلر توصیه می‌کند که نوع عناصر مجموعه را صریحاً مشخص کنید. برای حل مشکل بالا کافی است نوع متغیر موقت (خط 32) را به شکل زیر مشخص کنید :

from Person p in persons

LINQ برای انجام پرس جو هر کدام از اعضای مجموعه را به نوع Person تبدیل می‌کند. اگر شی ای از مجموعه پیدا شود که قابل تبدیل به نوع Person نباشد یک استثنا رخ می‌شود. عبارت from در ابتدای یک عبارت پرس وجو می‌آید این کار به Visual Studio امکان می‌دهد که نوع داده‌هایی که بر روی آنها کار می‌کنید را تشخیص دهد. همانطور که از شکل زیر پیداست، بعد از عبارت from می‌توانید از مزایای Intellisense بهره مند شوید.
from-clause-1010

وصل کردن چندین جدول از طریق چندین عبارت from

اگر شما 2 منبع داده در اختیار دارید، می‌توانید با استفاده از چند عبارت from آن‌ها را به همدیگر وصل کنید. مثال زیر را که باعث اتصال دو مجموعه از اعداد می‌شود را در نظر بگیرید :

int[] numberSet1 = { 1, 2, 3, 4, 5 };
int[] numberSet2 = { 6, 7, 8, 9, 10 };

var query = from n1 in numberSet1 
            from n2 in numberSet2 
            select String.Format("{0},{1}", n1, n2); 

foreach (var n in query)
{
    Console.WriteLine(n.ToString());
}
(1,6)
(1,7)
(1,8)
(1,9)
(1,10)
(2,6)
(2,7)
(2,8)
(2,9)
(2,10)
(3,6)
(3,7)
(3,8)
(3,9)
(3,10)
(4,6)
(4,7)
(4,8)
(4,9)
(4,10)
(5,6)
(5,7)
(5,8)
(5,9)
(5,10)

در مثال بالا از دو عبارت from که داده‌ها را از دو منبع مختلف واکشی می‌کنند استفاده کرده‌ایم. در انتها با استفاده از متد Format خروجی را فرمت بندی کرده‌ایم. همانطور که در خروجی می‌بینید، هر نوع ترکیب ممکن در خروجی پرس وجو قرار گرفته است (ضرب دکارتی). به این ترکیب اصطلاحاً inner join گفته می‌شود. همچنین شما می‌توانید مستقیماً از عبارت join که بعداً توضیح داده می‌شود استفاده کنید. به این نکته توجه کنید که لزومی ندارد عبارت from دوم و سوم مستقیماً بعد از عبارت from اول قرار گیرند. شما می‌توانید از پرس وجویی به شکل زیر استفاده کنید :

from n1 in numberSet1
where n1 > 2
from n2 in numberSet2
select String.Format("({0},{1})", n1, n2);

یک عبارت from تکی، معادل متدی در فضای نامی System.LINQ ندارد. شما می‌توانید از متد Select() و دیگر متدها استفاده کنید. ولی برای چند عبارت from می‌توانید از متد SelectMany() که در درس‌های بعدی به آن پرداخته می‌شود استفاده کنید .

ساخت ارتباط بر روی کلاس‌ها

عبارات پرس و جوی متشکل از چند عبارت from می‌توانند به شکل موثری در کلاس‌های مرتبط استفاده شوند. اما چنین کلاس‌هایی ابتدا باید به شکل مطلوب ساختار بندی شوند. به عنوان مثال یک مشتری می‌تواند چندین سفارش داشته باشد که این رابطه را می‌توان به عنوان یک رابطه یک به چند در نظر گرفت (یک مشتری با چندین سفارش) اجازه دهید ابتدا کلاس مشتری و سفارش را به شکل مناسب تعریف کنیم :

class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<Order> Orders { get; set; }
}

برای سادگی مثال، کلاس مشتری از 3 خاصیت تشکیل شده است. خاصیت سوم رابطه کلاس مشتری با کلاس سفارش را تعریف می‌کند. این خاصیت شامل لیست سفارش‌های مشتری است. کلاس سفارش در زیر تعریف شده است.

class Order
{
    public string Name { get; set; }
    public int Quantity { get; set; }
}

اجازه دهید چند شیء مشتری ایجاد کرده و چند سفارش را به خاصیت Orders آن‌ها اختصاص دهیم.

   1: class Program                                                                      
   2: {                                                                                  
   3:     static void Main(string[] args)                                                
   4:     {                                                                              
   5:         List<Customer> customers = new List<Customer>();               
   6:                                                                                    
   7:         Customer customer1 = new Customer()                                        
   8:         {                                                                          
   9:             FirstName = "John",                                                    
  10:             LastName = "Smith",                                                    
  11:             Orders = new List<Order>()                                       
  12:             {                                                                      
  13:                 new Order() { Name = "Pizza", Quantity = 5 },                      
  14:                 new Order() { Name = "Chicken", Quantity = 3 },                    
  15:                 new Order() { Name = "Salad", Quantity = 7 }                       
  16:             }                                                                      
  17:         };                                                                         
  18:                                                                                    
  19:                                                                                    
  20:         Customer customer2 = new Customer()                                        
  21:         {                                                                          
  22:             FirstName = "Allan",                                                   
  23:             LastName = "York",                                                     
  24:             Orders = new List<Order>()                                       
  25:             {                                                                      
  26:                 new Order() { Name = "Orange Juice", Quantity = 2 },               
  27:                 new Order() { Name = "Soda", Quantity = 4 }                        
  28:             }                                                                      
  29:         };                                                                         
  30:                                                                                    
  31:         Customer customer3 = new Customer()                                        
  32:         {                                                                          
  33:             FirstName = "Henry",                                                   
  34:             LastName = "Helms",                                                    
  35:             Orders = new List<Order>()                                       
  36:             {                                                                      
  37:                 new Order() { Name = "Speghetti", Quantity = 7 },                  
  38:                 new Order() { Name = "Nachos", Quantity = 13 }                     
  39:             }                                                                      
  40:         };                                                                         
  41:                                                                                    
  42:         customers.Add(customer1);                                                  
  43:         customers.Add(customer2);                                                  
  44:         customers.Add(customer3);                                                  
  45:                                                                                    
  46:         var results = from c in customers                                        
  47:                       from o in c.Orders                                         
  48:                       select new { c.FirstName, c.LastName, o.Name, o.Quantity };
  49:                                                                                    
  50:         foreach (var result in results)                                            
  51:         {                                                                          
  52:             Console.WriteLine("Name: {0} {1}, Order: {2}, Quantity: {3}",          
  53:                 result.FirstName, result.LastName, result.Name, result.Quantity);  
  54:         }                                                                          
  55:     }                                                                              
  56: }
Name: John Smith, Order: Pizza, Quantity: 5
Name: John Smith, Order: Chicken, Quantity: 3
Name: John Smith, Order: Salad, Quantity: 7
Name: Allan York, Order: Orange Juice, Quantity: 2
Name: Allan York, Order: Soda, Quantity: 4
Name: Henry Helms, Order: Speghetti, Quantity: 7
Name: Henry Helms, Order: Nachos, Quantity: 13

همانطور که در خروجی می‌بینید، سفارشات تمامی مشتریان نشان داده شده است. همچنین برای مرتب کردن خروجی می‌توان از عملگر group-by استفاده کرد. در خط 40-7 در مثال قبل، 3 شیء Customer ایجاد کرده‌ایم که هر کدام دارای سفارشات مخصوص به خود هستند. سپس هر کدام از این 3 شیء را به لیست مشتریان اضافه کرده‌ایم. خطوط 48-46 پرس وجویی با 2 عبارت from را نشان می‌دهد. اولین عبارت from یک شیء Customer را از لیست مشتریان انتخاب می‌کند. دومین عبارت from تک تک اعضای خاصیت Orders مشتری انتخاب شده را بر می‌گرداند. عبارت select با استفاده از projection نام و نام خانوادگی مشتری، نام و تعداد سفارش را انتخاب می‌کند. خطوط 54-50 نتیجه را در خروجی چاپ می‌کنند.

طراحی کلاس‌ها به شکل بالا بسیار مناسب است زیرا تشخیص ارتباط بین اشیاء به راحتی امکان پذیر است. جداول موجود در پایگاه داده معمولاً از این نوع ارتباطات استفاده می‌کنند. ابزارهایی در Visual Studio وجود دارند که جدوال و ارتباطات بین آنها را به کلاس‌ها و ارتباط بین آنها تبدیل می‌کنند. در مبحث LINQ To SQL به این امر پرداخته شده است.