در این آموزش قصد داریم به یکی از مباحث مهم و پایهای در توسعه برنامههای مبتنی بر Entity Framework Core در Asp.Net Core بپردازیم. تفاوت بین IEnumerable و IQueryable. این دو رابط کاربردی به ظاهر مشابه هستند، اما تفاوتهای اساسیای در شیوه اجرا و تاثیر آنها بر عملکرد برنامه دارند. در دنیای برنامهنویسی با EF Core، کار با دیتابیس از طریق LINQ انجام میشود. اما اینکه یک کوئری به صورت IEnumerable یا IQueryable باشد، تفاوت زیادی در نحوهی اجرای آن، مصرف حافظه، و سرعت دارد.
اینکه یک کوئری به صورت IEnumerable یا IQueryable باشد، تفاوت زیادی در نحوهی اجرای آن، مصرف حافظه، و سرعت دارد.
IEnumerable از فضای نام System.Collections.Generic است. برای پیمایش مجموعههایی استفاده میشود که در حافظه قرار دارند. اجرای کوئری به صورت in-memory (در حافظه) انجام میشود. مناسب برای زمانی است که دادهها قبلاً از دیتابیس بارگذاری شدهاند.
var users = context.Users.ToList(); // اجرای کوئری در همین لحظه
var filtered = users.Where(u => u.Age > 25); // فیلتر در حافظه انجام میشود
IQueryable از فضای نام System.Linq است. نمایندهای از یک کوئری است که روی منبع دادهای قابل query شدن (مثل دیتابیس) اجرا میشود. اجرای کوئری به صورت deferred execution است. EF Core این کوئری را به SQL تبدیل و در سرور اجرا میکند.
var filtered = context.Users.Where(u => u.Age > 25); // هنوز اجرا نشده
var list = filtered.ToList(); // اجرای کوئری اینجا اتفاق میافتد
هدف اصلی از استفادهی IQueryable و IEnumerable این است که تا حد امکان پردازشها در سمت دیتابیس و از طریق IQueryable انجام شود، نه در حافظه برنامه با استفاده از IEnumerable. این رویکرد باعث بهینهسازی سرعت، کاهش مصرف حافظه و افزایش عملکرد کلی برنامه میشود.
| ویژگی | IEnumerable | IQueryable |
|---|---|---|
| اجرا | در حافظه (Client Side) | در دیتابیس (Server Side) |
| زمان اجرا | بلافاصله بعد از ToList | زمانی که نیاز باشد – deferred |
| ترجمه به SQL | خیر | بله |
| عملکرد | کندتر در دادههای بزرگ | بهینهتر |
| مناسب برای | کوئریهای ساده و دادههای موجود | کوئریهای پیچیده و فیلتر در دیتابیس |
بسیاری از توسعهدهندگان تازهکار در EF Core ابتدا از ToList استفاده میکنند، سپس با Where فیلتر میکنند. این یعنی تمام دادهها از دیتابیس بارگیری میشود و فیلتر در حافظه انجام میشود. این کار در دادههای بزرگ منجر به کاهش شدید عملکرد میشود.
❌ استفاده نادرست
var users = context.Users.ToList().Where(u => u.IsActive);
✅ استفاده درست
var users = context.Users.Where(u => u.IsActive).ToList();
در برخی موارد نیاز است بخشی از کوئری در دیتابیس اجرا شود و بخشی در حافظه. استفاده از AsEnumerable باعث میشود بخش دوم دیگر به SQL تبدیل نشود.
var query = context.Users.Where(u => u.IsActive); // IQueryable
var users = query.AsEnumerable().Where(u => SomeCustomFunction(u)); // IEnumerable
یک مثال از استفاده ترکیبی از IQueryable و IEnumerable: فرض کنید. در یک سایت فروشگاهی، کاربران میتونن لیست محصولات رو بر اساس دستهبندی و قیمت فیلتر کنن. این فیلترها مستقیماً به دیتابیس منتقل میشن (یعنی IQueryable) و SQL برای انتخاب محصولات اجرا میشه. اما بعد از واکشی، میخوای قیمت نهایی هر محصول رو بر اساس یک الگوریتم پیچیدهی تخفیفدهی حساب کنی. مثلاً تخفیف براساس نوع کاربر، مناسبت روز، موجودی انبار و غیره — که این الگوریتم در C# نوشته شده و قابل ترجمه به SQL نیست.