تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
[C#.NET] تطبيق على طرق LINQ - صانع اكواد SQL صغير mini SQL generator
#1
بسم الله الرحمن الرحيم 
السلام عليكم ورحمة الله وبركاته اعضاء المنتدى الكرام

الموضوع بلغة VB

لايخفى عليكم اخواني الكرام اهمية وقوة تقنية LINQ ، فهذه التقنية جعلت التعامل مع البيانات اسهل بكثير جدا ، فما كان يتطلب عديد السطور لانجازة اصبح الان يكفي سطر واحد لعمله


تقديم :

تقنيه LINQ ليست فقط لقواعد البيانات ، في الحقيقة هذه التقنيه تتعامل مع اي فئة تتضمن الواجهة IEnumerable اي قابلة للعد ، وهذا يشمل المصفوفات Arrays و القوائم generic lists

تتضمن هذه الواجهة طرق كثيرة جدا للتعامل مع البيانات ، مثل Select , Where , Aggregate وغيرها الكثير والكثير

لذلك فكرت انه من باب التطبيق نقوم بعمل مثال يقوم بانشاء استعلام SQL بناء على بيانات الجدول المختارة من المستخدم ، او ما يصطلح الاخوة على تسميته "صانع اكواد"

ويجب قبل البدأ ان اخبركم ان الهدف من هذا المثال هو التدرب على استخدام LINQ وليس هو شرح كيفية عمل برنامج صانع اكواد ، فهذا موضوع قد قتل بحثا من جانب الاخوة الكرام

ولاجل الوصول الى ذلك بابسط طريقة ممكنة هناك بعض المفاهيم التي يجب ان نتفق عليها اولا :


مفاهيم اساسية خارج LINQ : 


  1. string interpolation الادخال وسط النصوص : 

    تتيح لنا هذه الميزة ادخال القيم وسط النصوص بطريقة سهلة جدا واكثر امانا ، 
    مثلا اذا كان لدينا متغير اسمه i نريد ان ندخله وسط قيمة نصية فان المطلوب هو ان تسبق القيمة النصية ب العلامة $ وان تضع المتغير داخل قوسين {} :


             string str = $"I'm {i} years old";
  2. null-coalescing operator : 

    عندما تحاول قراءة قيمة متغير ما قيمته null فانك ستحصل على Exception بسبب ذلك ، طيب ماذا اذا كنت تريد ان تضع قيمة افتراضية عندما يكون المتغير ب null ترجع هذه القيمة بدلا من ان يحدث Exception ، هنا يأتي دور المعامل ? والمعامل ??

    المعامل ? يستخدم قبل نوع المتغير ويعني بان هذا المتغير يقبل القيم null 
    المعامل ?? يوضع بعد قراءة المتغير ويوضع بعده القيمة الافتراضية اذا كان المتغير ب null

    السطر التالي سيضع القيمة 0 في number اذا كان i ب null بدلا من ان يرمي ب خطأ

كود :
  int number = (int?)i ?? 0;


المطلوب :

 قيمة من نوع String تمثل نص الاستعلام بالشكل التالي حسب الصفوف المختارة :

PHP كود :
SELECT [Col1],[Col2],[Col3FROM [TableWHERE [Col1]='Value' AND [Col2]='Value' 


البداية :

نبدأ بتصميم بسيط يؤدي الغرض :

   

لاحظ الداتا جريد فيو قمنا باضافة 4 اعمدة لها عبارة عن : 
  • اسماء الاعمدة ،
  • هل هذا العمود داخل في جملة SELECT ،
  • هل هذا العمود داخل في قيمة WHERE ،
  • اذا كان داخل في جملة WHERE ما القيمة المطلوبة للشرط
الكود : 

دعونا اولا نفكر فيما سنحتاجه ،

صيغة الاستعلام التي نريدها تتكون من كلمات ثابتة : "SELECT" و "FROM" و "WHERE"  

وكلمات مغيرة : اسماء الحقول المختارة بعد SELECT وكذلك الشروط الموضوعة بعد WHERE وكذلك اسم الجدول

طيب لذلك سنقوم بتعريف 4 متغيرات من نوع String 
  • tableName : وهو يمثل اسم الجدول المطلوب انشاء قيمة استعلام له
  • selects : الحقول المطلوبة محاطة ب [] و مفصول بينها ب ,
  • wheres : حقول الشروط محاطة ب [] ايضا ومتبوعة بالقيمة على شكل ='value' و كذلك مفصول بينها ب AND
  • sqlCommand : نخزن فيه الاستعلام المنشأ

هيا بنا في حدث btnGenerateSql.Click نعرف متغير اسم الجدول ونسند له قيمة من مربع النص الخاص باسم الجدول محاط بالاقواس [] :


كود :
           string tableName = $"[{txtTableName.Text}]";

كيف نحصل على القيمة المطلوبة للمتغير selects ؟

طيب نبدأ العمل الحقيقي الان ، نريد الحصول على صفوف الداتا جريد فيو على شكل IEnumerable من نوع DataGridViewRow لنتمكن من استخدام طرق LINQ عليها ،

لفعل ذلك نستخدم الطريقة Cast ونمرر لها النوع DataGridViewRow :

كود :
           var selects = dgvTable.Rows
               .Cast<DataGridViewRow>();

جميل جدا ، الان ركزو معي جيدا ، نريد اختيار الصفوف المختارة فقط كجزء من Select ، اي الصفوف التي فيها قيمة الخلية الثانية ب True ، 

لفعل ذلك نحن بحاجة الى الطريقة Where ، التي هي دالة الاختيار ، وظيفتها انها تختار من عناصر بيانات العناصر التي توافق شرط معين ، تقبل مدخل تعبير لمدا يمثل الشرط الذي يتم الاخيار بناء عليه ، وهو في هذه الحالة قيمة الخلية الثانية من الصف الحالي x.Cells[1].Value 

   

كود :
           var selects = dgvTable.Rows
               .Cast<DataGridViewRow>()
               .Where(x => (bool)x.Cells[1].Value);

ولكن اذا كان الخلية لم يتم الضغط عليها قيمتها تكون null ويرمي البرنامج ب خطأ ، لذلك نستخدم المعاملين ? و ?? لتلافي ذلك :

كود :
           var selects = dgvTable.Rows
               .Cast<DataGridViewRow>()
               .Where(x => (bool?)x.Cells[1].Value ?? false);

الان حصلنا على الصفوف المطلوبة فقط ، جميل جدا نريد الان الحصول منها على اسماء الاعمدة المطلوبة ( قيمة الخلية الاولى في الصف ) محاطة ب [] ،

لفعل ذلك نستخدم الدالة Select التي تسمي دالة التحويل ، وظيفتها انها تحول كل العناصر التي تطبق عليها الى صورة اخرى ، في حالتنا نريد تحويل عناصر صفوف الداتا جريد فيو DataGridViewRow الى قيم نصية String تمثل اسماء الاعمدة ، الدالة تقبل مدخل تعبير لمدا يمثل التحويل المطلوب على كل عنصر ، وهو في هذه الحالة قيمة الخلية الاولى محاطة باقواس $"[{x.Cells[0].Value}]"

   

كود :
           var selects = dgvTable.Rows
               .Cast<DataGridViewRow>()
               .Where(x => (bool?)x.Cells[1].Value ?? false)
               .Select(x => $"[{x.Cells[0].Value}]");

طيب جميل جدا الان ماذا اذا لم يختار المستخدم اي صف ؟ في هذه الحالة تكون القائمة فارغة ، نريد في هذه الحالة ان تكون القيمة الافتراضية "*" هي العنصر الوحيد ، يعني في حالة لم يتم اختيار صف معين نحن سنعيد القيمة * التي تعني كل الاعمدة في SQL  ، 

لفعل ذلك نستخدم الدالة  DefaultIfEmpty التي وظيفتها انها ترجع بعنصر وحيد عبارة عن القيمة الافتراضية التي تمررها لها في حالة كانت القائمة التي تطبق عليها فارغة ، في هذه الحالة نمرر لها القيمة "*"

كود :
           var selects = dgvTable.Rows
               .Cast<DataGridViewRow>()
               .Where(x => (bool?)x.Cells[1].Value ?? false)
               .Select(x => $"[{x.Cells[0].Value}]")
               .DefaultIfEmpty("*");

طيب الان جميل جدا جدا جدا ، معنا قائمة فيها كل اسماء الاعمدة المختارة ، محاطة باقواس [] وفي حالة لم تيم اختيار شيء معنا * ،

الخطوة القادمة هي تجميع عناصر هذه القائمة في قيمة نصية واحدة مفصول بينهم ب ,

لفعل ذلك نستخدم الدالة Aggregate التي وظيفتها تجميع عناصر القائمة في قيمة واحدة ، تأخد مدخل تعبير لمدا ذو مدخلين ومخرج واحد

يطلب منك اذا كان لديك هذين العنصرين (x,y) مثلا كيف تقوم بتجميعهم ؟ 

نخبره اننا نريده ان يضع القيمة الاولى متبوعة بفاصلة متبوعة بالقيمة الثانية 

كود :
(x, y) => $"{x},{y}"


كود :
           var selects = dgvTable.Rows
               .Cast<DataGridViewRow>()
               .Where(x => (bool?)x.Cells[1].Value ?? false)
               .Select(x => $"[{x.Cells[0].Value}]")
               .DefaultIfEmpty("*")
               .Aggregate((x, y) => $"{x},{y}" );

حسنا الان انتهينا من المطلوب للمتغير selects ، الان وقت مناسب جدا لاخذ كوب ساخن من الشاي الثقيل عديم السكر  Big Grin

انتهيت ؟ مرحبا بك مجددا  Smile

بقي لنا ان نحصل على قيمة المتغير wheres 

نبدأ مثلما بدأنا سابقا ، نحول العناصر باستخدام Cast :


كود :
           var wheres = dgvTable.Rows
               .Cast<DataGridViewRow>();
 
نختار الصفوف المحددة فقط في الخلايا الثالثة ( CheckBox الخاص ب Where ) 
كود :
           var wheres = dgvTable.Rows
               .Cast<DataGridViewRow>()
               .Where(x => (bool?)x.Cells[2].Value ?? false);

   

بعد ذلك نحصل على قيم الصفوف المطلوب عمل شرط بها وكذلك احاطتها باقواس [] واضافة قيمة الشرط لها ='value'
الكود التالي نخبره ان يحول كل عنصر من القائمة الى ما نريده :
كود :
           var wheres = dgvTable.Rows
               .Cast<DataGridViewRow>()
               .Where(x => (bool?)x.Cells[2].Value ?? false)
               .Select(x => $"[{x.Cells[0].Value}]='{x.Cells[3].Value}'");

لاحظ ان x.Cells[0].Value تمثل اسم العمود بينما x.Cells[3].Value تمثل قيمة الشرط بعد Where=

   
   

نجعل القيمة الافتراضية 1=1 ، في حالة ليس هناك عناصر شرط نريد جلب الكل في الاستعلام :

كود :
           var wheres = dgvTable.Rows
               .Cast<DataGridViewRow>()
               .Where(x => (bool?)x.Cells[2].Value ?? false)
               .Select(x => $"[{x.Cells[0].Value}]='{x.Cells[3].Value}'")
               .DefaultIfEmpty("1=1");

واخيرا تجميع العناصر والفصل بينهم  ب AND

كود :
           var wheres = dgvTable.Rows
               .Cast<DataGridViewRow>()
               .Where(x => (bool?)x.Cells[2].Value ?? false)
               .Select(x => $"[{x.Cells[0].Value}]='{x.Cells[3].Value}'")
               .DefaultIfEmpty("1=1")
               .Aggregate((x, y) => $"{x} AND {y}");

اخر خطوة هي وضع العناصر في متغير الاستعلام :

كود :
           string sqlCommand = $"SELECT {selects} FROM {tableName} WHERE {wheres}";

وبعد ذلك اخراجها للمستخدم : 

كود :
           txtSql.Text = sqlCommand;

حسنا انتهينا فعلا ،

النتيجة :

   

مع عدم اختيار اي شيء :

   

اتمنى اكون قد وفقني الله لتبسيط المعلومة ، انا جاهز للاسئلة بخصوص هذا الموضوع
اتمنى ان ينتشر استخدام LINQ بين الاخوة العرب


الملفات المرفقة
.zip   MiniSqlGenerator.zip (الحجم : 44.22 ك ب / التحميلات : 5)
الرد
تم الشكر بواسطة: kiki , حريف برمجة , asemshahen5 , Hasaneen , محمد كريّم


المواضيع المحتمل أن تكون متشابهة .
الموضوع : الكاتب الردود : المشاهدات : آخر رد
  مشروع صغير لعيادات فحص النظر Rabeea Qbaha 7 2,053 25-03-20, 09:12 PM
آخر رد: لسه مبتدئ
  Abu Ehab Barcode Generator Abu Ehab 7 2,765 20-07-18, 12:25 PM
آخر رد: Abu Ehab
  Abu Ehab BarCode Generator Abu Ehab 3 2,218 28-03-18, 07:11 PM
آخر رد: honeesh
  [سؤال] الرجاء مساعدتي في الحصول على تطبيق للتقويم wwwww 0 1,015 28-11-14, 09:51 PM
آخر رد: wwwww

التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم