متد زير را كه يكي از اشتباهات رايج حين استفاده از LINQ خصوصا جهت Binding اطلاعات است، در نظر بگيريد:
IQueryable<Customer> GetCustomers()
اين متد در حقيقت هيچ چيزي را Get نميكند! نام اصلي آن GetQueryableCustomers و يا GetQueryObjectForCustomersاست.
IQueryable قلب LINQ است و تنها بيانگر يك عبارت (expression) از ركوردهايي ميباشد كه مد نظر شما است و نه بيشتر.
IQueryable<Customer> youngCustomers = repo.GetCustomers().Where(m => m.Age < 15);
به آن بايد به شكل يك expression builder نگاه كرد و نه ليستي از اشياء فيلتر شدهي ما. به اين مفهوم، deferred execution (اجراي به تاخير افتاده) نيز گفته ميشود (بايد دقت داشت كه IQueryable هم يك نوع IEnumerable است به علاوه expression trees كه مهمترين وجه تمايز آن نيز ميباشد).
براي مثال در عبارت زير تنها در زمانيكه متد ToList فراخواني ميشود، كل عبارت LINQ ساخته شده، به عبارت SQL متناظر با آن ترجمه شده، اطلاعات از ديتابيس اخذ گرديده و حاصل به صورت يك ليست بازگشت داده ميشود:
IList<Competitor> competitorRecords = competitorRepository
.Competitors
.Where(m => !m.Deleted)
.OrderBy(m => m.countryId)
.ToList(); //فقط اينجا است كه اس كيوال نهايي توليد ميشود
در مورد IEnumerable ها چطور؟
IEnumerable<Product> products = repository.GetProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
لطفا ابتدا به بانك اطلاعاتي رجوع كن و تمام ركوردهاي محصولات موجود را بازگشت بده. سپس بر روي اين حجم بالاي اطلاعات، محصولاتي را كه قيمت بالاي 25 دارند، فيلتر كن.
اگر همين دو سطر را با IQueryable بازنويسي كنيم چطور؟
IQueryable<Product> products = repository.GetQueryableProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
بديهي است اين روش منابع كمتري را نسبت به حالتي كه تمام اطلاعات ابتدا دريافت شده و سپس فيلتر ميشوند، مصرف ميكند (حالت بازگشت تمام اطلاعات ممكن است شامل 20000 ركورد باشد، اما حالت دوم شايد فقط 5 ركورد را بازگشت دهد).
سؤال: پس IQueryable بسيار عالي است و از اين پس كلا از IEnumerable ها ديگر نبايد استفاده كرد؟
خير! توصيه اكيد طراحان اين است كه لطفا تا حد امكان متدهايي كه IQueryable بازگشت ميدهند ايجاد نكنيد! IQueryable يعني اينكه اين نقطهي آغازين كوئري در اختيار شما، بعد برو هر كاري كه دوست داشتي با آن در طي لايههاي مختلف انجام بده و هر زمانيكه دوست داشتي از آن يك خروجي تهيه كن. خروجي IQueryable به معناي مشخص نبودن زمان اجراي نهايي كوئري و همچنين مبهم بودن نحوهي استفاده از آن است. به همين جهت متدهايي را طراحي كنيد كه IEnumerable بازگشت ميدهند اما در بدنهي آنها به نحو صحيح و مطلوبي از IQueryable استفاده شده است. به اين صورت حد و مرز يك متد كاملا مشخص ميشود. متدي كه واقعا همان فيلتر كردن محصولات را انجام ميدهد، همان 5 ركورد را بازگشت خواهد داد؛ اما با استفاده از يك ليست يا يك IEnumerable و نه يك IQueryable كه پس از فراخواني متد نيز به هر نحو دلخواهي قابل تغيير است.