IEnumerable<T> مقابل IQueryable<T>: أيهما تختار في C#؟ تحليل كامل

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

IEnumerable<T> مقابل IQueryable<T>: مبارزة تحدد مصير تطبيقك

\n\n

هل تساءلت يومًا لماذا استعلام قاعدة البيانات الخاص بك بطيء، على الرغم من أن البيانات قليلة؟ أو لماذا بعد إضافة سطر واحد من الكود يتحول التطبيق إلى سلحفاة؟ على الأرجح، واجهت المعضلة الكلاسيكية: IEnumerable<T> أم IQueryable<T>؟ هذان ليسا مجرد واجهتين من عالم C# و LINQ. بل هما عالمين مختلفين، نهجين مختلفين للعمل مع البيانات. أحدهما للمجموعات المحلية، والآخر للمصادر البعيدة مثل قواعد البيانات. في هذه المقالة، لن نعدد الاختلافات فحسب، بل سنحللها بأمثلة حقيقية، حتى لا تقع في فخ الأداء مرة أخرى.

\n\n

من هو من: المفاهيم الأساسية

\n\n

لنبدأ بالأساسيات. IEnumerable<T> هي واجهة تمثل سلسلة من العناصر في الذاكرة. وهي مثالية للعمل مع المجموعات مثل List<T> و Array و Dictionary. عند استخدام IEnumerable، يتم تنفيذ جميع العمليات (التصفية، الترتيب) على جانب العميل، أي في تطبيقك.

\n\n

IQueryable<T> هي واجهة أكثر تقدمًا، مصممة للعمل مع مصادر البيانات الخارجية (قواعد البيانات، خدمات الويب). لا تخزن البيانات، بل تبني شجرة تعبيرات (Expression Tree). وهذا يسمح لموفر LINQ (مثل Entity Framework) بتحويل كود C# الخاص بك إلى استعلام SQL فعال وتنفيذه على جانب الخادم.

\n\n

العبارة الرئيسية التي يجب تذكرها: IEnumerable يعمل في الذاكرة، IQueryable يعمل على جانب مصدر البيانات.

\n\n

الفرق الرئيسي: أين ومتى يتم تنفيذ المنطق

\n\n

تخيل أنك بحاجة إلى تصفية قائمة من 10,000 منتج أغلى من 1000 روبل، وترتيبها حسب السعر.

\n\n

مثال 1: IEnumerable (تصفية في الذاكرة)

\n
// لنفترض أن products هي List<Product> (IEnumerable)\nvar expensiveProducts = products\n    .Where(p => p.Price > 1000)\n    .OrderBy(p => p.Price)\n    .ToList();\n
\n

ماذا يحدث هنا؟ أولاً، يتم تحميل جميع المنتجات العشرة آلاف إلى الذاكرة. ثم يطبق C# عامل التصفية Where والترتيب OrderBy في تطبيقك. إذا كان لديك 10,000 سجل، فهذا ليس حرجًا. ولكن إذا كان لديك 10 ملايين، فسينهار التطبيق أو يتجمد.

\n\n

مثال 2: IQueryable (تصفية على خادم قاعدة البيانات)

\n
// لنفترض أن dbContext.Products هي DbSet<Product> (IQueryable)\nvar expensiveProducts = dbContext.Products\n    .Where(p => p.Price > 1000)\n    .OrderBy(p => p.Price)\n    .ToList();\n
\n

هنا السحر: IQueryable لا يقوم بتحميل البيانات فورًا. بدلاً من ذلك، يبني استعلام SQL: SELECT * FROM Products WHERE Price > 1000 ORDER BY Price. يتم إرسال هذا الاستعلام إلى قاعدة البيانات، وتقوم بإرجاع البيانات المصفاة والمرتبة فقط. في الذاكرة، تحصل فقط على ما تحتاجه. توفير هائل في الموارد.

\n\n

مثال عملي: فخ ToList()

\n\n

من أكثر الأخطاء شيوعًا للمطورين المبتدئين هو الاستدعاء المبكر لـ ToList() أو ToArray(). هذا يحول IQueryable إلى IEnumerable.

\n\n

كود سيء (يقتل الأداء):

\n
var allProducts = dbContext.Products.ToList(); // تحميل جميع البيانات إلى الذاكرة!\nvar filteredProducts = allProducts.Where(p => p.Price > 1000).ToList();\n
\n

في هذا المثال، نقوم أولاً بتحميل جدول Products بأكمله إلى الذاكرة، ثم نقوم بتصفيته. هذا يعادل تفريغ المستودع بأكمله في موقف السيارات، ثم البحث عن الصندوق المناسب في الكومة.

\n\n

كود جيد (أمثل):

المدونات

توصيات الكتب