You are on page 1of 61

‫بسم هللا الرحمن الرحيم‬

‫‪.‬‬

‫تتلخص أهدفنا في التعامل مع قواعد البيانات في النقاط التالية ‪:‬‬


‫‪ -1‬االتصال بمصدر قاعدة البيانات ‪.‬‬
‫‪ -2‬قراءة البيانات التي بها وعرضها بطرق شتى من خالل األدوات المتوفرة‬
‫في بيئة‪. Microsoft.NET‬‬
‫‪ -3‬معالجة البيانات وتحديثها من خالل تعديل قيم هذه البيانات أو إضافة‬
‫بيانات جديدة أو حذف بيانات غير مرغوب فيها ‪.‬‬
‫‪ -4‬حفظ البيانات المحدثة ‪.‬‬
‫‪ -5‬اإلبحار في البيانات من خالل االنتقال للسجل التالي أو ألعودة للسجل‬
‫السابق أو االنتقال للسجل األول أو االنتقال للسجل األخير ‪.‬‬
‫‪ -6‬البحث عن البيانات بسهولة ويسر من خالل وضع فالتر للترشيح ‪.‬‬
‫‪ -7‬ربط البيانات ذات العالقة في أكثر من جدول وعرضها ومعالجتها ‪.‬‬
‫‪ -8‬طباعة التقارير الملخصة للبيانات ‪.‬‬
‫=============‬
‫البداية ‪-:‬‬
‫* نبذه عن ‪:ADO.NET‬‬
‫هي مجموعة من الفئات مشمولة في مجال األسماء ‪System.Data‬‬
‫غرضها الوصول إلى مصادر البيانات‪ Data Sources‬والتي تمثل بيانات‬
‫محفوظة تحت أنظمة قواعد بيانات متعددة األنواع ( مثل ‪Microsoft Office‬‬
‫‪ Access‬أو ‪ SQL Server‬أو ‪ ) Oracle‬مما يعني قدرتك على الوصول إلى أي‬
‫قاعدة بيانات مهما كانت الشركة المنتجة لها ‪ ( .‬أ‪ .‬تركي العسيري) ‪.‬‬
‫وتقوم ‪ ADO.NET‬باستخدام مزودات البيانات لالتصال بمصادر البيانات ومن‬
‫ثم استرجاع هذه البيانات وتعديلها وإعادتها لمصادرها وحفظها هناك ( أ‪.‬‬
‫خالد الجديع ) ‪.‬‬
‫* االتصال بقواعد البيانات‪-:‬‬
‫للوصول إلى البيانات المخزنة في قاعدة البيانات ( والتي سوف تكون في‬
‫درسنا هنا عبارة عن ملف من نوع ‪ MDB‬الخاص ببرنامج ‪Microsoft Office‬‬
‫‪ ) Access‬والقراءة منها أو الكتابة فيها يجب أن نكوّن اتصال ناجح معها ‪،‬‬
‫وذلك يتم من خالل كائن االتصال المسمى ‪ ، Connection‬ولتجهيز هذا‬
‫الكائن نحتاج إلى أعطائه معلومات عن ملف قاعدة البيانات الذي نريد أن‬
‫نتصل به ( من أهمها على سبيل المثال مزود البيانات وخادم البيانات‬
‫واسم ومكان قاعدة البيانات و كلمة المرور مع كلمة السر إن وجدت ) ‪،‬‬
‫وهذه المعلومات تكوّن لنا بما يسمى سلسلة االتصال ( ‪Connection‬‬
‫‪ ) String‬وهي عبارة عن أحد خصائص كائن االتصال ‪.‬‬
‫* سلسلة االتصال ‪:‬‬
‫وهي عبارة عن مجموعة من العوامل ( ‪ ) Parameters‬الضرورية لالتصال‬
‫بقواعد البيانات ‪ ،‬وتختلف هذه العوامل بنا ًء على نوع مزود البيانات الذي‬
‫سوف نتحدث عنه باألمثلة في السطور التالية ‪ ،‬ومن أهم ما يهمنا من‬
‫هذه العوامل ما يلي ‪:‬‬
‫‪ : Provider -‬وهو عبارة عن اسم مزود البيانات وسوف نذكر هنا نوعين‬
‫األكثر انتشاراً ‪ ،‬النوع األول ‪ Microsoft.Jet.OLEDB.4.0‬وهو للتعامل مع‬
‫قواعد بيانات برنامج ‪ ، Microsoft Office Access‬والثاني ‪ SQLOLEDB‬وهو‬
‫مخصص للتعامل مع قواعد بيانات برنامج ‪. SQL Server‬‬
‫‪ : DataSource -‬وهو اسم خادم البيانات ( أللذي يحتوي على جداول‬
‫البيانات التي نريد أن نتصل بها ) ‪ ،‬ونجد أن خادم البيانات في برنامج‬
‫‪ Microsoft Office Access‬هو اسم ملف قاعدة البيانات الذي يحمل امتداد‬
‫‪ ، MDB‬بينما برنامج ‪ SQL Server‬فهو عبارة عن اسم الـ ‪ Server‬المثبت‬
‫على الجهاز ‪.‬‬
‫‪ : Intial Catalog -‬وهو عبارة عن اسم قاعدة البيانات الموجوده في‬
‫الخادم بالنسبة لبرنامج ‪. SQL Server‬‬
‫‪ : UserID/Password -‬وهو عبارة عن اسم المستخدم وكلمة المرور‬
‫لقاعدة البيانات في حالة وجود قيود أمنية عليها ‪.‬‬
‫ولعل المثالين التاليين تجعل الصورة واضحة جداً ‪:‬‬
‫مثال ‪: 1‬‬
‫سلسة اتصال بملف قاعدة بيانات برنامج ‪.. Microsoft Office Access‬‬

‫كود‬
‫‪Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\ MymdbFile.mdb;User‬‬
‫;‪ID=Admin;Jet OLEDB:Database Password=You'rePassword‬‬

‫مثال ‪: 2‬‬
‫سلسة اتصال بملف قاعدة بيانات برنامج ‪.. SQL Server‬‬

‫كود‬
‫‪Provider=SQLOLEDB;Data Source=SQL Server DB;Initial‬‬
‫‪Catalog=Northwind;User ID=You'reUserName;Password=You'rePassword‬‬

‫ي طريقة‬
‫بعد التعرف على سلسلة االتصال يجب أن نتعرف على نوع ّ‬
‫االتصال بقاعدة البيانات ‪..‬‬
‫ي طريقة االتصال بقواعد البيانات ‪:‬‬
‫* نوع ّ‬
‫من الجدير بالذكر أننا لو أردنا أن نتصل على قاعدة البيانات بواسطة‬
‫‪ ADO.NET‬يجب أن نختار طريقة االتصال المناسبة الحتياجاتنا ‪ ،‬فلدينا‬
‫طرقتين لالتصال وهما االتصال المتصل ( ‪ ) Connection Oriented‬و االتصال‬
‫المنفصل ( ‪ . ) Connectionless Oriented‬ولكل منهما امتيازاته وعيوبه و‬
‫إستراتجياته الخاصة به في العمل ‪ ،‬غير أن األخير يعتبر األمثل في التعامل‬
‫مع قواعد البيانات ‪ ،‬وهنا بالتحديد يضيع أغلب من يرغبون تعلم برمجة‬
‫قواعد البيانات من خالل ‪. ADO.NET‬‬
‫‪-----------------------------------------------------------------------------------‬‬

‫توقفنا في حديثنا الماضي أن أغلب من يرغبون تعلم برمجة قواعد البيانات‬


‫من خالل ‪ ADO.NET‬يضيعون في التفريق بين االتصال المتصل (‬
‫‪ ) Connection Oriented‬و االتصال المنفصل ( ‪Connectionless‬‬
‫‪ . ) Oriented‬والسبب في اعتقادي هو ضعف فهم اآللية التي يعمل بها‬
‫كل منهما من ناحية ‪ ،‬والخلط بينهما من ناحية أخرى ‪ .‬ولكي نزيل هذه‬
‫المشكلة سوف نتحدث عن كل منهما بشكل مستقل ‪..‬‬
‫وقبل ذلك يجب أن نوضح أمر مهم وهو ‪ :‬أنه عند بدأ مشروع جديد لالتصال‬
‫بقاعدة البيانات يجب قبل كل شيء أن نستدعي فضاء األسماء المناسب‬
‫لمزود البيانات (‪ ) Provider‬الذي نتعامل معه ‪ ،‬والذي تندرج تحته كائناته‬
‫الخاصة به لالتصال بقاعدة البيانات و ومعالجتها ‪ ،‬ومن مميزات استدعاء‬
‫فضاء األسماء أنه يغنينا عن كتابة مسار الكائنات التي سوف نستخدمها ‪،‬‬
‫فقط نكتب اسم الكائن ‪..‬‬

‫فعند التعامل مع قواعد بيانات ‪ Microsoft Office Access‬نستدعي فضاء‬


‫األسماء ‪ System.Data.OleDb‬وذلك بكتابة الكود التالي في أعلى صفحة‬
‫الكود ‪:‬‬

‫كود‬
‫‪Imports System.Data.OleDb‬‬

‫وتندرج تحته مجموعة من الكائنات ولكن كل ما يهمنا منها كمبتدئين ما‬


‫يلي ‪:‬‬
‫‪OleDbCommand , OleDbConnection , OleDbDataAdapter ,‬‬
‫‪OleDbDataReader , OleDbParameter , OleDbCommandBuilder ,‬‬
‫‪. OleDbTransaction. , OleDbException‬‬

‫وعند التعامل مع قواعد بيانات ‪ SQL Server‬نستدعي فضاء األسماء‬


‫‪ System.Data.SqlClient‬وذلك بكتابة الكود التالي في أعلى صفحة الكود ‪:‬‬

‫كود‬
‫‪Imports System.Data. SqlClient‬‬

‫وتندرج تحته مجموعة من الكائنات ولكن ما يهمنا منها كمبتدئين ما يلي ‪:‬‬
‫‪SqlCommand , SqlConnection , SqlDataAdapter , SqlDataReader ,‬‬
‫‪SqlParameter , SqlCommandBuilder , SqlTransaction.‬‬
‫‪. SqlException‬‬

‫مالحظة ‪ :‬اإلخوة الذين يستخدمون ‪ SQL Server‬يستطيعون أن يكملوا معنا‬


‫الدرس دون مشاكل ‪ ،‬وكل ما عليهم هو أن يستبدلوا عبارة ‪ OleDb‬بعبارة‬
‫‪. Sql‬‬

‫* االتصال المتصل (‪: ) Connection Oriented‬‬

‫وتتلخص فكرته في أن نقوم بإجراء اتصال مع قاعدة البيانات ‪ ،‬وبعد قبوله‬


‫نقوم بفتح االتصال ثم نقوم بتنفيذ مجموعة من األوامر التي نحتاجها ( مثل‬
‫قراءة البيانات من أجل عرضا للمستخدم ‪ ،‬أو تحديث البيانات من إضافة و‬
‫تعديل حذف وحفظ ‪ ،‬أو إلغاء عملية التحديث ‪ ،‬أو البحث عن بيانات معينة أو‬
‫أإلبحار والتجول في البيانات ) وبعد االنتهاء نقوم بإغالق االتصال ‪ ،‬ويعتبر‬
‫إغالق االتصال ضرورياً الن معظم مصادر البيانات تدعم عدداً محدوداً من‬
‫االتصاالت المفتوحة ‪ .‬وهذا يعني أننا يجب أن نحافظ على االتصال حتى‬
‫ننتهي من الهدف الذي تم من أجله االتصال ‪.‬‬
‫وهو مناسب " في حاله بناء التطبيقات المفردة ‪ ،‬أي عندما يكون للنظام‬
‫مستخدم واحد وتوجد واجه المستخدم ومخزن قاعدة البيانات على نفس‬
‫الجهاز ‪ ( " .‬أ‪ ، SOLO.NET .‬مشرف منتدى ال ‪) VB.NET‬‬

‫وال يمكن استخدامه في حالة التطبيقات التي يحتاجها الكثير من‬


‫المستخدمين ‪ ،‬ويرجع السبب في ذلك إلى أن مثل هذا النوع من االتصال‬
‫يستهلك جزءاً مهم من موارد النظام ‪ ،‬ويترتب علية تدني أداء هذه‬
‫التطبيقات ‪ .‬وألن معظم مصادر البيانات تدعم عدداً محدوداً من االتصاالت‬
‫المفتوحة كما أسلفنا سابقاً ‪ .‬ويعد ذلك من أكبر عيوب هذا النوع باإلضافة‬
‫إلى أنه يتطلب كتابة الكثير من األكواد البرمجية كما سوف نشاهد في‬
‫الجزء العملي من الدرس ‪ ،‬وذلك يعد عائق كبير أمام صيانة الكود وتحديثه‬
‫من وقت ألخر ‪.‬‬
‫ولعمل اتصال من هذا النوع نحتاج إلى ثالث كائنات أساسية نستخدمها‬
‫على النحو التالي ‪:‬‬

‫‪ -1‬كائن االتصال ‪ : OleDbConnection‬من أجل االتصال بقاعدة البيانات ‪.‬‬


‫‪ -2‬فتح االتصال بقاعدة البيانات ‪.‬‬
‫‪ -3‬كائن األوامر ‪ : OleDbCommand‬في اعتقادي أنه يمثل القلب لالتصال‬
‫المتصل ‪ ،‬وهو من أجل تنفيذ عملية قراءة البيانات المخزنة في قاعدة‬
‫البيانات ‪ ،‬أو تحديثها ( من إضافة وتعديل وحذف ) ‪ ،‬ويعتمد في تكوينه على‬
‫وجود اتصال مفتوح مع قاعدة البيانات ‪ ،‬وعلى جملة االستعالم البنيوية‬
‫‪ ( SQL‬وهي لغة قياسية لبناء ومعالجة قواعد البيانات ومحتوياتها ‪ ،‬وتتضمن‬
‫هذه اللغة تعليمات من أجل إضافة ‪ ،‬تعديل ‪ ،‬حذف ‪ ،‬ترتيب ‪ ،‬و اختيار‬
‫سجالت ) ‪ .‬ومن أهم خصائصه ‪-:‬‬

‫‪ : Connection -‬ويجب أن يحمل اسم االتصال الذي سوف يستخدمه كائن‬


‫األمر ‪.‬‬

‫‪ : CommandType -‬وله ثالثة قيم ‪ ،‬القيمة ‪ ( Text‬لتنفيذ جملة استعالم‬


‫بنيوية ‪ ، ) SQL‬القيمة ‪ ( StoredProcedur‬لتنفيذ إجراء مخزن في قاعدة‬
‫البيانات ) ‪ ،‬القيمة ‪ ( TableDairect‬للتعامل مع جدول واحد ) ‪.‬‬
‫مالحظة ‪ :‬سوف نستخدم في درسنا القيمة ‪ Text‬فقط وهي ما تهمنا‬
‫كمبتدئين ‪.‬‬

‫‪ : CommandText -‬ويعتمد على القيمة التي اخترناها في الخاصية‬


‫السابقة ( ‪ ، )CommandType‬لذلك سوف يكون لدينا ثالث مدخالت‬
‫متناسبة معها على التوالي ‪ ،‬اإلدخال األول عبارة عن جملة استعالم‬
‫بنيوية والتي سوف يتم تنفيذها سواء الختيار أو تحديث السجالت ‪،‬‬
‫المدخل الثاني عبارة عن اسم اإلجراء المخزن في قاعدة البيانات والذي‬
‫نريد تنفيذه ‪ ،‬والمدخل الثالث عبارة عن اسم الجدول الذي نريد التعامل‬
‫معه ‪.‬‬

‫‪ : Parameters -‬ويستخدم للوصول إلى معامالت اإلدخال و اإلخراج والقيم‬


‫العائدة ‪.‬‬

‫بعد تجهيز الخصائص السابقة لكائن األمر ونريد تنفذ األعمال المطلوبة منه‬
‫عند حصوله على تيار من البيانات ‪ ،‬لدينا ثالثة طرق يتم بها العمل ‪:‬‬

‫الطريقة األولى ( ‪ ) ExecuteReader‬وهي أحد خصائص كائن األمر وتقوم‬


‫بتنفيذ استعالم بنيوي يرجع لنا مجموعة من الصفوف الموجودة في قاعدة‬
‫البيانات وهو ما يتطلب استرجاع هذه النتيجة بواسطة الكائن‬
‫‪ OleDbDataReader‬والذي سون نتحدث عنه بعد قليل ‪.‬‬

‫الطريقة الثانية ( ‪ ) ExecuteNonQuery‬وهي أحد خصائص كائن األمر‬


‫وتقوم بتنفيذ استعالم بنيوي يقوم بتحديث قاعدة البيانات ( من إضافة ‪،‬‬
‫تعديل ‪ ،‬و حذف ) أو تكوين قاعدة بيانات والنتيجة التي ترجع هي عدد‬
‫الصفوف المتأثرة باألمر ‪.‬‬
‫الطريقة الثالثة ( ‪ ) ExecuteScalar‬وهي أحد خصائص كائن األمر وتقوم‬
‫بتنفيذ استعالم بنيوي يرجع لنا قيمة واحده فقط من قاعدة البيانات ‪.‬‬

‫‪ -4‬كائن قراءة البيانات ‪ : OleDbDataReader‬ويقوم بقراءة تيار من البيانات‬


‫القابلة للقراءة فقط وفى اتجاه واحد يكون لألمام فقط ‪ .‬ويعتمد في تكوينه‬
‫على وجود كائن أمر مبني على جملة استعالم مصممة الختيار سجالت ‪.‬‬
‫ومن أهم خصائصه ‪:‬‬

‫‪ : Read -‬ومهمتها هي توجه قارئ البيانات إلى قراءة الصف األول من‬
‫النتائج ‪ ،‬وترجع القيمة ‪ True‬إذا وجد صف أو ‪ False‬إذا لم يوجد صف ‪.‬‬

‫‪ : Item -‬ومهمتها هي استرجاع قيمة العمود إما باستخدام أسم العمود أو‬
‫رقمه ‪.‬‬

‫‪ : GetValues -‬ومهمتها هي استرجاع قيمة الصف كامال ً ‪.‬‬

‫‪ : isDBnull -‬ومهمتها هي اختبار فيما إذا كانت البيانات تحتوي على قيم‬
‫‪. Null‬‬

‫‪ : Close -‬ومهمته هي إغالق كائن قارئ البيانات وال يقوم بإغالق االتصال ‪.‬‬
‫مع مالحظة أنه يجب دائماً إغالق قارئ البيانات عند االنتهاء منه الن كائن‬
‫األوامر إذا كان يحتوي على معامالت إخراج أو قيم جديدة لن يكون في‬
‫اإلمكان التعامل معها إلى أن يتم االنتهاء من إغالق كائن ‪. DataReader‬‬

‫‪ -5‬إغالق االتصال بقاعدة البيانات ‪.‬‬

‫* وهنا نصل للجانب العملي من الدرس ‪:‬‬

‫سوف نركز في الجانب العملي على الثالثة طرق التي يتم بها العمل في‬
‫كائن األوامر ‪ OleDbCommand‬والذي قلنا فيما سبق أنه يمثل القلب ‪.‬‬

‫مثال ‪ : 1‬الطريقة ‪ ، ExecuteReader‬ماذا لو فرضنا أننا نريد أن نقرأ مجموعة‬


‫من البيانات الموجودة في ملف قاعدة البيانات مصمم ببرنامج ‪Microsoft‬‬
‫‪ Office Access‬والمسمى ( ‪ ) Note‬والذي من المفترض أن يوجد في‬
‫نفس مسار مشروعنا ‪ ،‬وهذا الملف محمي بكلمة المرور ( ‪ ) ado.net‬وهو‬
‫يحتوي على جدول اسمه ( ‪ ) Information‬والذي يحتوي بدوره على‬
‫الحقول التالية ( ‪ ID‬ويحمل خاصية الترقيم التلقائي ويعد مفتاح أساسي ‪،‬‬
‫‪ Name‬ويحمل خاصية كونه نص ‪ Phone ،‬ويحمل خاصية كونه رقم ) ‪ ،‬ثم‬
‫نعرض هذه البيانات في مربعات نص ‪ ،‬فكما تعلما سوف يكون العمل على‬
‫النحو التالي ‪...‬‬
‫أوال ً ‪ :‬نقوم بفتح مشروع جديد و نضيف مربعات النص التي سوف نعرض‬
‫فيها قيم الحقول الموجودة في الجدول ‪... Information‬‬

‫ثانياً ‪ :‬استدعاء فضاء األسماء الخاص بمزود بيانات برنامج ‪Microsoft Office‬‬
‫‪ ، Access‬وذلك بكتابة الكود التالي في أعلى صفحة الكود ‪..‬‬

‫كود‬
‫‪Imports System.Data.OleDb‬‬

‫ثالثاً ‪:‬‬
‫التصريح عن الكائنات التي نحتاجها كمتغيرات في منطقة ‪Declarations‬‬
‫في صفحة الكود وذلك كي يتم التعرف علية في مختلف أجزار النموذج‬
‫‪ ،‬في البداية نقوم بتهيئة كائن االتصال وليكن اسمه ‪ ، Conn‬كما في الكود‬
‫التالي ‪...‬‬

‫كود‬
‫‪Dim Conn As New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data‬‬
‫‪Source=C:\ ConnectedMode\Note.mdb;User ID=Admin;Jet OLEDB:Database‬‬
‫)"‪Password=ado.net‬‬

‫ولكي ال نجعل برنامجنا الذي نتدرب علية مقيد بمسار ثابت وهو " \‪C:‬‬
‫‪ " ConnectedMode\MyNote.mdb‬نستطيع أن نجعله أكثر مرونة بحيث‬
‫يعمل على أي مسار شرط أن يتواجد ملف قاعدة البيانات والملف التنفيذي‬
‫للبرنامج في نفس الموقع ‪ ،‬وذلك من خالل العبارة التالية "|‬
‫‪ "|DataDirectory‬بحيث يصبح الكود السابق كما يلي ‪..‬‬

‫كود‬
‫‪Dim Conn As New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data‬‬
‫‪Source=|DataDirectory|Note.mdb;User ID=Admin;Jet OLEDB:Database‬‬
‫)"‪Password=ado.net‬‬

‫ثم نقوم بالتصريح عن كائن األوامر وليكن اسمه ‪ MyCmd‬كما في الكود‬


‫التالي ‪..‬‬

‫كود‬
‫‪Dim MyCmd As New OleDbCommand‬‬

‫ثم نقوم بالتصريح عن كائن القراءة وليكن اسمه ‪ MyReader‬كما في الكود‬


‫التالي ‪..‬‬

‫كود‬
‫‪Dim MyReader As OleDbDataReader‬‬
‫رابعاً ‪ :‬في حدث تحميل النموذج نقوم بفتح االتصال ‪..‬‬

‫كود‬
‫‪Conn .Open‬‬

‫خامساً ‪ :‬نقوم بتهيئة كائن األمر ( باستخدام جملة استعالم بنيوية تقوم‬
‫بتحديد حقول معينة من الجدول الموجود في قاعدة البيانات ) كما ذكرنا‬
‫في الجزء ألنضري من الدرس ‪ ،‬وذلك من خالل الكود التالي ‪..‬‬

‫كود‬
‫‪MyCmd.Connection = Conn‬‬
‫‪MyCmd.CommandType = CommandType.Text‬‬
‫‪MyCmd.CommandText = "SELECT ID, Name, Phone FROM Information ORDER BY‬‬
‫";‪Name‬‬

‫سادساً ‪ :‬نقوم بتهيئة كائن القراءة ‪ ،‬وذلك بإسناد الطريقة‬


‫‪ ( ExecuteReader‬التابعة لكائن األمر ‪ ) MyCmd‬إلية ‪ ،‬كما في الكود ‪..‬‬

‫كود‬
‫‪MyReader = MyCmd.ExecuteReader‬‬

‫سابعاً ‪ :‬قبل إسناد قيم الحقول إلى مربعات النص نتحقق من أن كائن‬
‫القراءة وجد صفوف تحتوي على بيانات من خالل أداة الشرط ‪IF .. Then ..‬‬
‫‪ Else .. End If‬وباستخدام الخاصية ‪ ، Read‬وفي حالة توفر البيانات نقوم‬
‫بإسناد قيمة كل حقل لمربع النص الخاص به من خالل الخاصية ‪، Item‬‬
‫كما في الكود التالي ‪..‬‬

‫كود‬
‫‪If MyReader.Read Then‬‬
‫)"‪   txtID.Text = MyReader.Item("ID‬‬
‫)"‪   txtName.Text = MyReader.Item("Name‬‬
‫)"‪   txtPhone.Text = MyReader.Item("Phone‬‬
‫‪Else‬‬
‫)‪   MsgBox(" Empty ", MsgBoxStyle.Exclamation‬‬
‫‪End If‬‬

‫ثامناً ‪ :‬إغالق كال ً من كائن القراءة وكائن االتصال ‪ ،‬كما في الكود التالي ‪...‬‬

‫كود‬
‫)(‪MyReader.Close‬‬
‫)(‪Conn.Close‬‬

‫مثال ‪ : 2‬الطريقة ‪ ، ExecuteNonQuery‬استنادا للمثال السابق ماذا لو‬


‫أردنا تحديث بياناتنا ( إضافة ‪ ،‬تعديل ‪ ،‬و حذف ) ‪ ،‬إذا فلنتابع شرح هذا‬
‫المثال ‪..‬‬
‫أوال ً ‪ :‬نضيف ثالثة (‪ ، ) Button‬ونسمي األول ‪ btnAddNew‬والثاني‬
‫‪ btnUpdate‬والثالث ‪. btnDelete‬‬
‫ثانياً ‪ :‬في حدث النقر لـ ‪ btnAddNew‬نقوم بما يلي ‪..‬‬
‫‪ -1‬نقوم بفتح االتصال ‪..‬‬

‫كود‬
‫‪Conn .Open‬‬

‫‪ -2‬نقوم بتهيئة كائن أمر جديد ونسميه ‪ ( InsertCommand‬باستخدام‬


‫جملة استعالم بنيوية تقوم بإضافة بيانات مخزنة في ‪ ( Parameters‬ونرمز‬
‫لها بالرمز ? ) لحقول معينة من الجدول الموجود في قاعدة البيانات ) كما‬
‫ذكرنا في الجزء ألنضري من الدرس ‪ ،‬وذلك من خالل الكود التالي ‪..‬‬

‫كود‬
‫‪Dim InsertCommand As New OleDbCommand‬‬
‫‪InsertCommand.Connection = Conn‬‬
‫‪InsertCommand.CommandType = CommandType.Text‬‬
‫)‪InsertCommand.CommandText = "INSERT INTO Information(Name,Phone‬‬
‫";)?‪VALUES(?,‬‬

‫مالحظة ‪ :‬لم نضيف أي قيمة للحقل ‪ ID‬ألنه له خاصية الترقيم التلقائي ‪.‬‬

‫‪ -3‬نقم بإعطاء قيم للـ ‪ Parameters‬الموجودة في جملة االستعالم من‬


‫خالل الخاصية ‪ Parameters‬التابعة لكائن األمر ‪ ،‬وذلك بعد تنظيفه من إي‬
‫‪ ( Parameters‬فيما إذا كانت هناك أي ‪ Parameters‬مستخدمة سابقاً ) ‪..‬‬

‫كود‬
‫)(‪InsertCommand.Parameters.Clear‬‬
‫)‪InsertCommand.Parameters.AddWithValue("?", txtName.Text‬‬
‫)‪InsertCommand.Parameters.AddWithValue("?", txtPhone.Text‬‬

‫‪ -4‬نطبق الطريقة ‪ ExecuteNonQuery‬والتي سوف تقوم بتنفيذ استعالم‬


‫تحديث الجدول ‪..‬‬

‫كود‬
‫)(‪InsertCommand.ExecuteNonQuery‬‬

‫‪ -5‬نتخلص من كائن األمر ‪ InsertCommand‬ألننا انتهينا منه وحتى ال‬


‫يستهلك جزاء من موارد النظام ‪ ،‬ثم نغلق االتصال بقاعدة البيانات ونعرض‬
‫رسالة بنجاح العملية ‪..‬‬

‫كود‬
‫)(‪InsertCommand.Dispose‬‬
‫)(‪Conn.Close‬‬
‫)‪MsgBox(" OK ", MsgBoxStyle.Information‬‬
‫ثالثاً ‪ :‬في حدث النقر لـ ‪ btnUpdate‬نقوم بما يلي ‪..‬‬
‫‪ -1‬نقوم بفتح االتصال ‪..‬‬

‫كود‬
‫‪Conn .Open‬‬

‫‪ -2‬نقوم بتهيئة كائن أمر جديد ونسميه ‪ ( UpdateCommand‬باستخدام‬


‫جملة استعالم بنيوية تقوم بتحديث بيانات مخزنة في ‪ ( Parameters‬ونرمز‬
‫لها بالرمز ? ) لحقول معينة من الجدول الموجود في قاعدة البيانات وذلك‬
‫اعتمادا على قيمة الحقل ‪ ID‬كمعرف للسجل الذي نريد تحديثه ) ‪ ،‬وذلك‬
‫من خالل الكود التالي ‪..‬‬

‫كود‬
‫‪Dim UpdateCommand As New OleDbCommand‬‬
‫‪UpdateCommand.Connection = Conn‬‬
‫‪UpdateCommand.CommandType = CommandType.Text‬‬
‫?=‪UpdateCommand.CommandText = "UPDATE Information set Name=? , Phone‬‬
‫";?= ‪WHERE Id‬‬

‫مالحظة ‪ :‬لم نقوم بتعديل قيمة الحقل ‪ ID‬ألنه له خاصية الترقيم‬


‫التلقائي ‪.‬‬

‫‪ -3‬نقم بإعطاء قيم للـ ‪ Parameters‬الموجودة في جملة االستعالم من‬


‫خالل الخاصية ‪ Parameters‬التابعة لكائن األمر ‪ ،‬وذلك بعد تنظيفه من إي‬
‫‪ ( Parameters‬فيما إذا كانت هناك أي ‪ Parameters‬مستخدمة سابقاً ) ‪..‬‬

‫كود‬
‫)(‪UpdateCommand.Parameters.Clear‬‬
‫)‪UpdateCommand.Parameters.AddWithValue("?", txtName.Text‬‬
‫)‪UpdateCommand.Parameters.AddWithValue("?", txtPhone.Text‬‬
‫)‪UpdateCommand.Parameters.AddWithValue("?", txtID.Text‬‬

‫‪ -4‬نطبق الطريقة ‪ ExecuteNonQuery‬والتي سوف تقوم بتنفيذ استعالم‬


‫تحديث الجدول ‪..‬‬

‫كود‬
‫)(‪UpdateCommand.ExecuteNonQuery‬‬

‫‪ -5‬نتخلص من كائن األمر ‪ UpdateCommand‬ألننا انتهينا منه وحتى ال‬


‫يستهلك جزاء من موارد النظام ‪ ،‬ثم نغلق االتصال بقاعدة البيانات ونعرض‬
‫رسالة بنجاح العملية ‪..‬‬

‫كود‬
‫)(‪UpdateCommand.Dispose‬‬
‫)(‪Conn.Close‬‬
‫)‪MsgBox(" OK ", MsgBoxStyle.Information‬‬

‫رابعاً ‪ :‬في حدث النقر لـ ‪ btnDelete‬نقوم بما يلي ‪..‬‬


‫‪ -1‬نقوم بفتح االتصال ‪..‬‬

‫كود‬
‫‪Conn .Open‬‬

‫‪ -2‬نقوم بتهيئة كائن أمر جديد ونسميه ‪ ( DeleteCommand‬باستخدام‬


‫جملة استعالم بنيوية تقوم بحذف السجل الحالي من قاعدة البيانات وذلك‬
‫اعتمادا على قيمة الحقل ‪ ID‬كمعرف للسجل الذي نريد حذفه والتي‬
‫سوف نقوم بتخزينها في ‪ ( Parameter‬ونرمز له بالرمز ? ) ) ‪ ،‬وذلك من‬
‫خالل الكود التالي ‪..‬‬

‫كود‬
‫‪Dim DeleteCommand As New OleDbCommand‬‬
‫‪DeleteCommand.Connection = Conn‬‬
‫‪DeleteCommand.CommandType = CommandType.Text‬‬
‫" ;? = ‪DeleteCommand.CommandText = " DELETE FROM Information WHERE id‬‬

‫‪ -3‬نقم بإعطاء قيمة للـ ‪ Parameter‬الموجود في جملة االستعالم من‬


‫خالل الخاصية ‪ Parameters‬التابعة لكائن األمر ‪ ،‬وذلك بعد تنظيفه من إي‬
‫‪ ( Parameters‬فيما إذا كانت هناك أي ‪ Parameters‬مستخدمة سابقاً ) ‪..‬‬

‫كود‬
‫)(‪DeleteCommand.Parameters.Clear‬‬
‫)‪DeleteCommand.Parameters.AddWithValue("?", txtID.Text‬‬

‫‪ -4‬نطبق الطريقة ‪ ExecuteNonQuery‬والتي سوف تقوم بتنفيذ استعالم‬


‫تحديث الجدول ‪..‬‬

‫كود‬
‫)(‪DeleteCommand.ExecuteNonQuery‬‬

‫‪ -5‬نتخلص من كائن األمر ‪ DeleteCommand‬ألننا انتهينا منه وحتى ال‬


‫يستهلك جزاء من موارد النظام ‪ ،‬ثم نغلق االتصال بقاعدة البيانات ونعرض‬
‫رسالة بنجاح العملية ‪..‬‬

‫كود‬
‫)(‪DeleteCommand.Dispose‬‬
‫)(‪Conn.Close‬‬
‫)‪MsgBox(" OK ", MsgBoxStyle.Information‬‬
‫مثال ‪ : 3‬الطريقة ‪ ، ExecuteScalar‬استنادا للمثال األول ماذا لو أردنا‬
‫معرفة عدد السجالت المخزنة في الجدول ‪ ، Information‬إذا فلنتابع شرح‬
‫هذا المثال ‪..‬‬
‫أوال ً ‪ :‬نضيف ‪ ، Button‬ونسميه ‪. btnCountId‬‬
‫ثانياً ‪ :‬في حدث النقر لـ ‪ btnCountId‬نقوم بما يلي ‪..‬‬
‫‪ -1‬نقوم بفتح االتصال بقاعدة البيانات ‪..‬‬

‫كود‬
‫)(‪Conn.Open‬‬

‫‪ -2‬نقوم بتهيئة كائن أمر جديد ونسميه ‪ ( CountIdCommand‬باستخدام‬


‫جملة استعالم بنيوية تقوم بإعطائنا عدد السجالت المخزنة في الجدول‬
‫‪ ، ) Information‬وذلك من خالل الكود التالي ‪..‬‬

‫كود‬
‫‪Dim CountIdCommand As New OleDbCommand‬‬
‫‪CountIdCommand.Connection = Conn‬‬
‫‪CountIdCommand.CommandType = CommandType.Text‬‬
‫‪CountIdCommand.CommandText = "SELECT Count(id) AS CountID FROM‬‬
‫";‪Information‬‬

‫‪ -3‬نقم بالتصريح عن متغير من نوع ‪ String‬ونسميه ‪ CountId‬على سبيل‬


‫المثال ‪ ،‬وذلك كي نخزن فيه القيمة التي سوف يعود بها كائن األمر بعد أن‬
‫نمرر له الخاصية ‪.. ExecuteScalar‬‬

‫كود‬
‫)(‪Dim CountId As String = CountIdCommand.ExecuteScalar‬‬

‫‪ -4‬نقوم بعرض ‪ MsgBox‬يبين لنا القيمة المخزنة في المتغير ‪CountId‬‬


‫وهي هنا عدد السجالت الموجودة في الجدول ‪.. Information‬‬

‫كود‬
‫)‪MsgBox(CountId, MsgBoxStyle.Information‬‬

‫‪ -5‬نتخلص من كائن األمر ‪ CountIdCommand‬ألننا انتهينا منه وحتى ال‬


‫يستهلك جزاء من موارد النظام ‪ ،‬ثم نغلق االتصال بقاعدة البيانات ونعرض‬
‫رسالة بنجاح العملية ‪..‬‬

‫كود‬
‫)(‪InsertCommand.Dispose‬‬
‫)(‪Conn.Close‬‬
‫)‪MsgBox(" OK ", MsgBoxStyle.Information‬‬

‫الكائن الرابع ‪ ، Transaction :‬لم أحب أن ضيفه للكائنات الثالثة األولى‬


‫ألني أعتقد بأنه فرعي وليس أساسي ‪ ،‬و وضيفته أمنيه بحته ‪ ،‬حيث‬
‫يستخدم هذا الكائن غالبا عند عملية تحديث مجموعة سجالت مرتبطة مع‬
‫بعضها دفعة واحدة ‪ ،‬فإذا فشل في تحديث أحدهم يتم إلغاء العملية‬
‫بالكامل ‪ ،‬على سبيل المثال ‪ :‬في الحسابات البنكية ‪ ،‬عند سحب مبلغ‬
‫من حساب أحد األشخاص و إيداعه في حساب شخص أخر ‪ ،‬فثل هذه‬
‫العملية يجب أن تحدث دفعة واحده ‪ ،‬ولو حدث أي مشكله فنية في‬
‫العملية يجب إلغاء هذه العملية بالكامل ‪.‬‬
‫ومن أهم خصائص الكائن ‪ Transaction‬هي الخصيتان (‪ Commit‬و‬
‫‪. ) Rollback‬‬
‫حيث أن الخاصية ‪ Commit‬تدل على نجاع العملية بدون مشاكل أو أي‬
‫أخطاء وتسمح بتحديث البيانات ‪ ،‬و في حاله عدم تحقق شرط معين أو‬
‫حدوث خطأ فإن الخاصية ‪ Rollback‬تقوم بعمل تراجع وتلغي العملية ‪.‬‬
‫ولكي نهيئ الكائن ‪ Transaction‬يجب أن نصرح عنه كمتغير كما في‬
‫الكود ‪...‬‬

‫كود‬
‫‪Dim MyTransaction As OleDbTransaction‬‬

‫ثم نستدعي الخاصية ‪ BeginTransaction‬التابعة لكائن االتصال ونسندها‬


‫له ‪ ،‬كما يلي ‪..‬‬

‫كود‬
‫‪MyTransaction = Conn.BeginTransaction‬‬

‫‪ ،‬ثم نهيئ كائن األمر ( على سبيل المثال ‪ ) InsertCommand‬الذي نريد‬


‫منه تحديث البيانات بإضافة سجل جديد ‪ ،‬وذلك باستدعاء الخاصية‬
‫‪ Transaction‬التابعة له وإسناد الكائن ‪ Transaction‬له ‪..‬‬

‫كود‬
‫‪InsertCommand.Transaction = MyTransaction‬‬

‫وبعد تنفيذ الخاصية ‪ ExecuteNonQuery‬لكائن األمر نقوم بإستدعاء‬


‫الخاصية ‪ Commit‬التابعة للكائن ‪ Transaction‬وذلك لتأكيد قبول العملية‬
‫ونجاحها ‪..‬‬

‫كود‬
‫)(‪MyTransaction.Commit‬‬
‫وفي حالة وجود أي مشكلة نستدعي الخاصية ‪ Rollback‬التابعة للكائن‬
‫‪ Transaction‬وذلك للقيام بإلغاء العملية ‪...‬‬

‫كود‬
‫)( ‪MyTransaction.Rollback‬‬

‫لذلك ننصح تعشيش كل العمليات التي يتوقع وجود مشاكل بها داخل دالة‬
‫اكتشاف األخطاء ‪. Try...Catch…Finally‬‬

‫المثال الرابع ‪ :‬الكائن ‪ ، Transaction‬بالرجوع للمثال الثاني ونريد تعديل‬


‫بيانات السجل الحالي ونريد أن نستخدم الكائن ‪ Transaction‬كي نضمن‬
‫عدم حدوث أخطاء وأن كل البيانات المطلوبة قد تم تحديثها ‪ ،‬والتراجع عن‬
‫التحديث بالكامل في حالة لم يتم تحديث حقل من الحقول ‪ .‬وللقيام بذلك‬
‫نتبع األتي ‪..‬‬
‫في حدث النقر لـ ‪ btnUpdate‬نقوم بما يلي ‪..‬‬
‫‪ -1‬نقوم بالتصريح عن متغيرين ‪ ،‬األول كائن ‪ Transaction‬ونعطيه االسم‬
‫‪ ، MyTransaction‬والثاني ‪ Command‬ونعطيه االسم‬
‫‪... UpdateCommand‬‬

‫كود‬
‫‪Dim MyTransaction As OleDbTransaction‬‬
‫‪Dim UpdateCommand As New OleDbCommand‬‬

‫‪ -2‬نقوم بتعشيش دالة اكتشاف األخطاء ‪ ( Try...Catch…Finally‬مع‬


‫مالحظة أننا في جزئية الدالة ‪ Catch‬قمنا بالتصريح عن متغير أسميناه‬
‫‪ Filed‬من نوع ‪ OleDbException‬وهو من الكائنات التابعة لفضاء األسماء‬
‫‪ System.Data.OleDb‬ونستفيد منه في الحصول على معلومات عن سبب‬
‫الخطأ عند حدوثه في مصدر البيانات ‪ ) .‬كما يلي ‪..‬‬

‫كود‬
‫‪Try‬‬
‫العمليات التي نرغب في اصطياد أخطائها إن وجدت ‪'  ‬‬
‫‪       Catch Filed As OleDbException‬‬
‫األخطاء التي تم اصطيادها ‪ ،‬باإلضافة للتصحيح المناسب لها '‬
‫‪       Finally‬‬
‫العمليات التي يجب تنفيذها على كل حال '‬
‫‪End Try‬‬

‫مالحظة ‪ :‬في الجزء الذي يحتوي على العمليات التي نرغب في اصطياد‬
‫أخطائها إن وجدت من دالة ‪ Try‬نقوم بما يلي ‪..‬‬
.. ‫ نقوم بفتح االتصال‬-3

‫كود‬
Conn .Open

‫ استنادا لما تعلمناه في السطور السابقة نقوم بإعداد وتجهيز كال ً من‬-4
‫ ( مع مالحظة أننا استخدمنا هذه‬Command ‫ والكائن‬Transaction ‫الكائن‬
‫ لوحده ثم‬Name ‫ لتحديث الحقل‬ExecuteNonQuery ‫المرة الخاصية‬
‫ مع تعمد كتابة خطأ في‬، Phone ‫استخدمناها مر ًة أخرى لتحديث الحقل‬
" ‫ وبالتحديد في الكلمة‬Phone ‫جملة االستعالم التي نحدث بها الحقل‬
‫ وذلك حتى نتعرف على الطريقة‬، ""ABDATE ‫" والتي كتبناها‬UPDATE
... ) ‫التي سوف يتم بها اصطياد الخطأ وكيف سوف يتم تصحيحه‬

‫كود‬
MyTransaction = Conn.BeginTransaction
UpdateCommand.Connection = Conn
UpdateCommand.Transaction = MyTransaction
UpdateCommand.CommandType = CommandType.Text
UpdateCommand.CommandText = "UPDATE Information set Name=? WHERE Id
=?;"
UpdateCommand.Parameters.Clear()
UpdateCommand.Parameters.AddWithValue("?", txtName.Text)
UpdateCommand.Parameters.AddWithValue("?", txtID.Text)
UpdateCommand.ExecuteNonQuery()
UpdateCommand.CommandText = "ABDATE Information set Phone=? WHERE Id
=?;"
UpdateCommand.Parameters.Clear()
UpdateCommand.Parameters.AddWithValue("?", txtPhone.Text)
UpdateCommand.Parameters.AddWithValue("?", txtID.Text)
UpdateCommand.ExecuteNonQuery()

‫ وذلك‬Transaction ‫ التابعة للكائن‬Commit ‫ نقوم باستدعاء الخاصية‬-5


.. ‫لتأكيد قبول العملية ونجاحها مع عرض رسالة تفيد بنجاح العملية‬

‫كود‬
MyTransaction.Commit()
MsgBox(" OK ", MsgBoxStyle.Information)

‫ والذي يحتوي على‬Try ‫ من دالة‬Catch ‫ في الجزء المسمى‬: ‫مالحظة‬


‫ باإلضافة للتصحيح المناسب لها نقوم بما يلي‬، ‫األخطاء التي تم اصطيادها‬
..
‫ وذلك للقيام‬Transaction ‫ التابعة للكائن‬Rollback ‫ نستدعي الخاصية‬-6
‫ ثم نعرض رسالة نصية توضح لنا معلومات عن الخطأ الذي‬، ‫بإلغاء العملية‬
... ‫وقع أثناء تنفيذ البرنامج‬

‫كود‬
‫)(‪MyTransaction.Rollback‬‬
‫)‪MsgBox(Filed.Message, MsgBoxStyle.Exclamation‬‬

‫مالحظة ‪ :‬في الجزء المسمى ‪ Finally‬من دالة ‪ Try‬والذي يحتوي على‬


‫العمليات التي يجب تنفيذها على كل حال ‪ ،‬نقوم بما يلي ‪..‬‬
‫‪ -7‬نتخلص من كائن األمر ‪ UpdateCommand‬ألننا انتهينا منه وحتى ال‬
‫يستهلك جزاء من موارد النظام ‪ ،‬ثم نغلق االتصال بقاعدة البيانات ‪..‬‬

‫كود‬
‫)(‪UpdateCommand.Dispose‬‬
‫)(‪Conn.Close‬‬

‫مالحظة ‪ :‬لقد استفدت كثيراً في شرح هذا الكائن من درس قدمه األخ‬
‫‪ koao‬لذلك أحب أن أشكره على ذلك ‪.‬‬

‫** هنا ينتهي حديثنا اليوم ‪ ،‬ونأخذ قسطاً من الراحة ‪ ،‬لكي نستوعب هذه‬
‫الجرعة من المعلومات ونصحح أخطائها إن وجدت ‪ ،‬ونعلق على الغير‬
‫مفهوم منها ‪ ،‬و نكمل فيما بعد شرح ما تبقى من الدرس وذلك في‬
‫المشاركات القادمة ‪..‬‬

‫مالحظة ‪ :‬موضوعنا القادم يعد متعة برمجة قواعد البيانات الحقيقية ‪ ،‬وهو‬
‫االتصال المنفصل ( ‪ ، ) Connectionless Oriented‬والذي كما أسلفنا يعتبر‬
‫األمثل في التعامل مع قواعد البيانات ‪ ،‬ويغنينا عما سواه ‪ .‬إال أننا كان ال بد‬
‫لنا من تعلم كيفية التعامل مع االتصال المتصل (‪) Connection Oriented‬‬
‫ألنه يحل لنا بعض اإلشكاالت البرمجية في بعض البرامج ‪ ،‬رغم إننا لم‬
‫نتناول كل جوانبه ‪ ،‬ولكني أحب أن يعلم الجميع أنني أفرغت كل ما‬
‫بجعبتي من معلومات عنه ‪ ،‬ومن يملك المزيد فليكسب أجرنا ويعلمنا مما‬
‫علمه هللا ‪،‬‬

‫((سبحانك اللهم وبحمدك‪ ،‬أشهد أن ال إله إال أنت‪ ،‬أستغفرك وأتوب إليك ))‬
‫أمثلة الجزأ الثاني من الدرس توجد في الملف المرفق ‪..‬‬

‫* االتصال المنفصل ( ‪: ) Connectionless Oriented‬‬

‫يعد الوضع المنفصل هو األمثل للتعامل مع قواعد البيانات ‪ ،‬وذلك نظراً‬


‫لإلمكانات والكائنات المتنوعة التي تمكن المستخدم من أخذ نسخة من‬
‫الجداول المخزنة في قاعدة البيانات ‪ ،‬وقراءة البيانات المخزنة فيها ‪،‬‬
‫وتعديلها و عرضها وتصفيتها ومعالجتها أو إنشاء عالقات فيما بينها ‪ ،‬وذلك‬
‫لفترات طويلة ‪ ،‬دون أن تستهلك الكثير من موارد النظام ‪ ،‬ويترتب على ذلك‬
‫عمل التطبيقات بكفاءة عاليه ‪ .‬وألن معظم مصادر البيانات تدعم عدداً‬
‫محدوداً من االتصاالت المفتوحة ‪ ،‬فإن الوضع المنفصل استطاع أن يتجاوز‬
‫هذه المشكلة ‪ ،‬السيّما حين نستخدم تطبيقات متصلة بقواعد البيانات عبر‬
‫شبكة اإلنترنت ‪...‬‬

‫وتتلخص فكرته في أن نقوم بإجراء اتصال بقاعدة البيانات ‪ ،‬وذلك بواسطة‬


‫كائن االتصال ‪ Connection‬كما ذكرنا في الدروس السابقة ‪ ،‬وبعد قبول‬
‫االتصال نقوم بأخذ نسخة من البيانات المخزنة في مصدر البيانات "‪Data‬‬
‫‪ ( "Sources‬مصدر البيانات هنا هو ملف قاعدة البيانات بالنسبة لـ‬
‫‪ Microsoft Office Access‬أو السيرفر بالنسبة لـ ‪ ، ) SQL Server‬ثم نقوم‬
‫بتخزينها في الذاكرة ‪ ،‬وذلك من خالل كائن يكون بمثابة مستودع للبيانات‬
‫ويسمى ‪ ( DataSet‬مجموعة البيانات ) ‪ ،‬ولكي تتم هذه العملية يحتاج‬
‫هذا الكائن إلى كائن أخر يسمى ‪ ( DataAdapter‬موفّق البيانات ) يكون‬
‫بمثابة وسيط بين مجموعة البيانات "‪ " DataSet‬ومصادر البيانات "‪Data‬‬
‫‪ ، "Sources‬حيث يقوم ‪ DataAdapter‬بعملية فتح االتصال بقاعدة البيانات و‬
‫استيراد وتصدير البيانات من و إلى مصدرها األساسي ‪ ،‬وبعد تخزين‬
‫البيانات في ‪ ، DataSet‬فإنها تصبح منفصلة عن مصدر البيانات ‪ ،‬الن الكائن‬
‫‪ DataAdapter‬يقوم بشكل تلقائي أيضاً بقطع االتصال بقاعدة البيانات ‪،‬‬
‫وهذا االنفصال واالستقالل هو السبب في تسمية هذه الطريقة باالتصال‬
‫المنفصل أو الوضع المنفصل ‪ ،‬وهو السر في تفوقه على الوضع المتصل ‪،‬‬
‫ألنه يتغلب على جمع عيوبه ومشاكله ‪.‬‬

‫ومن ذلك نستطيع أن نقول أن الكائنات األساسية في الوضع المنفصل هي‬


‫‪. Connection ، DataSet ، DataAdapter :‬‬

‫وهناك أيضاً كائنات أخرى ليست فرعية ولكنها خدمية إن صح التعبير ‪،‬على‬
‫سبيل المثال " ‪DataTable ، DataRow ، DataColumn ، DataRelation ،‬‬
‫‪OleDbCommandBuilder ، BindingManagerBase ، CurrencyManager‬‬
‫‪، " ، BindingSource‬‬

‫ويأتي دورها في الغالب بعد وصول البيانات ( الجداول ) القادمة من مصادر‬


‫البيانات إلى ‪ ( DataSet‬مجموعة البيانات ) ‪ .‬وس ُنناقش بعضها ( أو كلها إذا‬
‫وجدنا الوقت الكافي لذلك ) وما يهمنا منها بإيجاز غير مخل إن شاء هللا ‪..‬‬

‫مالحظة ‪ :‬لسنا مجبرين على استخدام كل األدوات والخصائص التي سوف‬


‫ترد في الشرح ‪ ،‬ولكن ما يلزمنا منها فقط ‪ ،‬وسوف تتضح لنا الكثير من‬
‫األمور حين نصل إلى الجزء العملي من الدرس ‪.‬‬
‫‪-----------------------‬‬
‫‪ - 1‬الكائن ‪-: Connection‬‬
‫لقد تحدثنا عنه في الدروس السابقة بما فيه الكفاية ‪.‬‬

‫‪-: DataSet – 2‬‬


‫يمكن تعرف مجموعة البيانات ‪ Dataset‬بأنها مستودع أو ذاكرة وسيطة‬
‫لتخزين البيانات التي تم إحضارها من مصادر البيانات ‪ ،‬ويريد أن يستعملها‬
‫التطبيق ‪.‬‬

‫من كان قد برمج قواعد البيانات باستخدام اإلصدار السابق من ‪ ADO‬في‬


‫‪ VisualBasic6‬فهو يعرف تماماً الكائن ‪ Recordset‬حيث يعتبر نقطة التفاعل‬
‫الرئيسية لبرنامج ما مع قاعدة البيانات ‪ ،‬ويعالج هذا الكائن السجالت في‬
‫جدول واحد أو السجالت المختارة بواسطة استفسار ( جملة استعالم‬
‫بنيوية ) واحد ‪ .‬بينما نجد أن الكائن ‪ DataSet‬في اإلصدار الجديد من‬
‫‪ ADO.NET‬يمكنه تمثيل النتائج المتولدة عن عدة جداول و تتبع العالقات‬
‫بين هذه الجداول ‪ ،‬أو تمثيل النتائج المتولدة عن استفسارات عديدة ‪.‬‬
‫وبذلك نستطيع أن نقول أنه إذا كان ‪ Recordset‬يشكل جدول قاعدة‬
‫بيانات ‪ ،‬فإن ‪ DataSet‬يشكل قاعدة بيانات كاملة تجلس في الذاكرة ‪ .‬ولعل‬
‫ما في هذه النبذة ‪ ،‬ما يعطي القارئ تصور عام عن الكائن ‪ ، DataSet‬و‬
‫يزيل الجدل حول ‪ Recordset‬و ‪ DataSet‬وأيهما أفضل ‪.‬‬

‫وأالن سون نتطرق لبعض خصائص ‪: DataSet‬‬

‫* الخاصية ‪: Clear‬‬
‫تزيل هذه الخاصية كل البيانات من جميع جداول ‪ ، DataSet‬بينما تبقي‬
‫تعريفات الجداول وأي تقييدات معرفه من قبل ‪... DataSet‬‬

‫كود‬
‫‪MyDs.Clear‬‬

‫فإذا أردنا إزالة الجداول والتقييدات ‪ ،‬نقوم بإعادة إنشاء ‪ DataSet‬كما في‬
‫الكود التالي ‪..‬‬

‫كود‬
‫‪MyDs = New DataSet‬‬

‫الخاصية ‪: HasChanges‬‬
‫إن ‪ DataSet‬يتتبع فيما إذا كان هناك أي تغييرات قد أجريت على بياناته ‪،‬‬
‫ويمكن استدعاؤها بكل بساطه من خالل الكود التالي ‪..‬‬
‫كود‬
‫‪MyDs.HasChanges‬‬

‫وتعيد هذه الخاصية القيمة ‪ true‬إذا كانت البيانات قد حدثت إما لتغيير أو‬
‫إضافة أو حذف ‪ ،‬فإذا حمل برنامج ما بشكل أولي بيانات إلى ‪، DataSet‬‬
‫فإن البيانات لم تغيّر بعد وستعيد هذه الخاصية عندئذ القيمة ‪ False‬وعندما‬
‫دث المستخدم أي بيانات للجداول فإن هذه الخاصية سوف تعيد القيمة‬ ‫ح ِّ‬
‫ُي َ‬
‫‪ . true‬وإذا قام المستخدم بحفظ بياناته المحدثة ‪ ،‬فإنها تعيد القيمة ‪False‬‬
‫من جديد ‪..‬‬
‫فلنتأمل هذا الكود ‪..‬‬

‫كود‬
‫‪   If MyDS.HasChanges = True Then‬‬
‫‪  DataSet .‬نقوم بكتابة أي إجراء في حالة إذا كان هناك أي تغييرات قد أجريت على الـ '‪      ‬‬
‫‪   Else‬‬
‫‪ DataSet .‬نقوم بكتابة أي إجراء في حالة إذا لم يكن هناك أي تغييرات قد أجريت على الـ ' ‪    ‬‬
‫‪End If‬‬

‫ويمكن أن تأخذ هذه الخاصية وسيط اختياري يسمى ‪، DataRowState‬‬


‫يشير إلى نوع التغييرات التي قد حدثت ‪ ،‬حيث يمكن استخدام هذه‬
‫الطريقة لرؤية فيما إذا كانت هناك سجالت قد أضيفت ‪ ،‬أو عدلت ‪ ،‬أو‬
‫حذفت ‪ ،‬أو لم يقع عليها أي تغيير ‪ ،‬وذلك من خالل القيم التي يحملها هذا‬
‫الوسيط وهي على النحو التالي ‪..‬‬

‫‪ : Added -‬ويشير إلى الصفوف التي أضيفت إلى الـ ‪. DataSet‬‬


‫‪ : Deleted -‬ويشير إلى الصفوف التي قد حذفت من الـ ‪. DataSet‬‬
‫‪ : Modified -‬ويشير إلى الصفوف التي قد غيرت قيم بياناتها من الـ‬
‫‪. DataSet‬‬
‫‪ : Unchanged -‬ويشير إلى أن الصفوف لم تتغير من الـ ‪. DataSet‬‬
‫ولكي يتضح الشرح نتأمل هذا الكود ‪...‬‬

‫كود‬
‫‪If MyDataSet.HasChanges(DataRowState.Added) Then‬‬
‫‪.‬نقوم بكتابة أي إجراء في حالة إذا كان هناك صفوف جديدة قد أضيفت '‪      ‬‬
‫‪ElseIf MyDataSet.HasChanges(DataRowState.Deleted) Then‬‬
‫‪ .‬نقوم بكتابة أي إجراء في حالة إذا كان هناك صفوف قد حذفت '‪      ‬‬
‫‪ElseIf MyDataSet.HasChanges(DataRowState.Modified) Then‬‬
‫نقوم بكتابة أي إجراء في حالة إذا كان هناك صفوف قد تغيرت قيمها '‪      ‬‬
‫‪ElseIf MyDataSet.HasChanges(DataRowState.Unchanged) Then‬‬
‫‪ .‬نقوم بكتابة أي إجراء في حالة إذا لم يحدث أي تغيير على الصفوف '‪      ‬‬
‫‪End If‬‬

‫الخاصية ‪: AcceptChanges‬‬
‫تقوم هذه الخاصية بإعادة وضع الـ ‪ DataSet‬الذي تم التعديل لبياناته ‪،‬‬
‫ليشير إلى أن البيانات لم تعدل ‪ .‬جاعال ً االستدعاءات المستقبلية للخاصية‬
‫‪ HasChanges‬تعيد القيمة ‪ . False‬إنه من الهام جداً إدراك أن‬
‫‪ AcceptChanges‬ال تقوم بالواقع بتحديث قاعدة البيانات ‪ ،‬إنها فقط تعيد‬
‫وضع الـ ‪ DataSet‬المعدل ‪ .‬وبشكل نموذجي فإن أي برنامج سيقوم بتخزين‬
‫أية تغييرات إلى قاعدة البيانات ‪ ،‬ومن ثم يستدعي ‪ AcceptChanges‬بعد‬
‫ذلك مباشرة ‪.‬‬

‫كود‬
‫‪  ‬كود تصدير البيانات المحدثة من مجموعة البيانات إلى مصدر البيانات باستخدام موفّق البيانات '‬
‫‪MyDs.AcceptChanges‬‬

‫الخاصية ‪: RejectChanges‬‬
‫تقوم هذه الخاصية بإلغاء أية تغييرات قد أجريت على البيانات في الـ‬
‫‪ DataSet‬منذ تحميلها ‪ ،‬أو منذ أخر استدعاء لها ‪ ،‬بمعنى أنها تلغي أية‬
‫أسطر مضافة حديثاً ‪ ،‬وتستعيد أسطراً حذفت أيضاً حديثاً ‪ ،‬وتعيد وضع‬
‫األسطر المعدلة إلى قيمتها السابقة ‪ ،‬كما أنها تقوم بإعادة وضع الـ‬
‫‪ DataSet‬الذي تم التعديل لبياناته ‪ ،‬ليشير إلى أن البيانات لم تعدل ‪.‬‬
‫جاعال ً االستدعاءات المستقبلية للخاصية ‪ HasChanges‬تعيد القيمة‬
‫‪. False‬‬

‫كود‬
‫‪MyDs.RejectChanges‬‬

‫مالحظة ‪:‬‬
‫تقوم الخاصية ‪ AcceptChanges‬بإزالة المعلومات حول الحالة السابقة‬
‫للبيانات ‪ ،‬وبالتالي ال يمكن استخدام ‪ RejectChanges‬الستعادة آخر قيم‬
‫للبيانات ‪ ،‬هذا يعني أن ‪ RejectChanges‬سيؤدي إلى استعادة البيانات‬
‫بالحالة التي كانت عليها بعد آخر استدعاء للخاصية ‪. AcceptChanges‬‬

‫الخاصية ‪: GetChanges‬‬
‫تساعدنا هذه الخاصية في إنشاء كائن ‪ DataSet‬جديد بنفس بنية الكائن‬
‫األصلي ‪ ،‬ولكنه يحتوي فقط على سجالت تمثل التغييرات التي حدثت‬
‫للبيانات ‪ ،‬وله أيضاً وسيط إختياري ‪ ( DataRowState‬الذي تحدثنا عنه في‬
‫الخاصية ‪ ) HasChanges‬يخبر ‪ GetChanges‬فيما إذا يتوجب عليها إحضار‬
‫السجالت التي أضيفت أو عدلت أو حذفت أو تركت دون تغيير ‪ .‬وباإلمكان‬
‫جمع قيم ‪ DataRowState‬الختيار أكثر من نوع واحد من التغييرات التي‬
‫حدثت للبيانات داخل الكائن ‪. DataSet‬‬
‫ولها استخدامات كثيرة منها ‪:‬‬
‫‪ -1‬قلنا سابقاً ‪ ،‬نحن في الوضع المنفصل نستخدم الكائن ‪DataAdapter‬‬
‫إلعادة تخزين التغييرات الموجودة في ‪ DataSet‬إلى مصدر قاعدة البيانات ‪.‬‬
‫وذلك يتم من خالل الخاصية ‪ Update‬التابعة للكائن ‪ DataAdapter‬الذي‬
‫سوف نتحدث عنه فيما بعد ‪ ،‬ما يهمنا هنا هو أن هذه الخاصية تبحث خالل‬
‫‪ DataSet‬عن تغييرات وتخزنها واحداً تلو اآلخر ‪ .‬ويكون تخزين التغييرات‬
‫جمعت في مجموعات حسب نوعها ‪ ،‬وبكلمات أخرى فإن‬ ‫أسرع إذا ُ‬
‫الخاصية ‪ Update‬ستعطي إنجازاً أفضل ‪ ،‬إذا حفظت كل التعديالت أوال ً ‪ ،‬ثم‬
‫بعد ذلك كل اإلضافات وأخيراً كل المحذوفات ‪.‬‬
‫‪ -2‬أحياناً يحتاج برنامج ما تخزين تغييرات في ترتيب معين ‪ ،‬ليفي بمتطلبات‬
‫التقييدات العالئقية لجداول قاعدة البيانات ‪.‬‬

‫ولكي نفهم طريقة هذه الخاصية ‪ ،‬فلنتابع سوياً هذه األمثلة ‪..‬‬

‫مثال ‪ ( : 1‬إخبار المستخدم عن عدد السجالت الجديدة )‬


‫في هذا المثال نقوم بالتصريح عن كائن ‪ DataSet‬جديد ‪ ،‬ثم نختبر فيما إذا‬
‫كان الكائن األصلي يحتوي على أي تغييرات عليه ‪ ،‬وفي حالة وجود أي‬
‫تغيير نستخدم الخاصية ‪ GetChanges‬لتأهيل الكائن الجديد بنفس بنية‬
‫الكائن األصلي ‪ ،‬ويحتوي فقط على السجالت التي أضيفت ‪ ،‬وقبل أن نخبر‬
‫المستخدم عن عدد السجالت الجديدة نختبر الكائن الجديد لنتأكد من‬
‫احتوائه على بيانات ‪ ،‬وفي حالة وجود بيانات نخبر المستخدم بذلك ‪..‬‬

‫كود‬
‫‪   Dim ds_added As New DataSet‬‬
‫‪   If MyDS.HasChanges Then‬‬
‫)‪       ds_added = MyDS.GetChanges(DataRowState.Added‬‬
‫‪       If (Not (ds_added) Is Nothing) Then‬‬
‫)" ‪           MsgBox(ds_added.Tables(0).Rows.Count & " Records Added‬‬
‫‪       End If‬‬
‫‪End If‬‬

‫ويمكن قياس هذا المثال على باقي القيم (‪Deleted ، Modified ،‬‬
‫‪) Unchanged‬‬

‫مثال ‪( : 2‬جمع قيم ‪ DataRowState‬الختيار أكثر من نوع واحد من‬


‫التغييرات التي حدثت للبيانات داخل الكائن ‪) DataSet‬‬
‫في هذا المثال نقوم بالتصريح عن كائن ‪ DataSet‬جديد ‪ ،‬ثم نختبر فيما إذا‬
‫كان الكائن األصلي يحتوي على أي تغييرات عليه ‪ ،‬وفي حالة وجود أي‬
‫تغيير نستخدم الخاصية ‪ GetChanges‬لتأهيل الكائن الجديد بنفس بنية‬
‫الكائن األصلي ‪ ،‬ويحتوي فقط على السجالت التي أضيفت أو حذفت ‪،‬‬
‫وقبل أن نخبر المستخدم عن عدد السجالت الجديدة أو المحذوفة ‪ ،‬نختبر‬
‫الكائن الجديد لنتأكد من احتوائه على بيانات ‪ ،‬وفي حالة وجود بيانات نخبر‬
‫المستخدم بذلك ‪..‬‬

‫كود‬
‫‪   Dim ds_AddedOrDeleted As New DataSet‬‬
‫‪   If MyDS.HasChanges Then‬‬
‫‪       ds_AddedOrDeleted = MyDS.GetChanges(DataRowState.Added Or‬‬
‫)‪DataRowState.Deleted‬‬
‫‪       If (Not (ds_AddedOrDeleted) Is Nothing) Then‬‬
‫‪           MsgBox(ds_AddedOrDeleted.Tables(0).Rows.Count & " Records Added or‬‬
‫)" ‪Deleted‬‬
‫‪       End If‬‬
‫‪End If‬‬

‫مثال ‪ ( : 3‬تحديث التغييرات حسب نوعها ‪ ،‬لتخزين البيانات بشكل أسرع )‬

‫مالحظة ‪ :‬نرجو التركيز على الجزء الخاص بـ ‪ GetChanges‬حتى تتضح‬


‫الصورة الكاملة في أذهاننا من خال متابعة باقي الدروس ‪ ،‬بعدها يمكننا‬
‫فهم باقي تفاصيل الكود الغامضة في هذا الوقت ‪.‬‬

‫في هذا المثال نقوم بالتصريح عن كائنين ‪ DataSet‬جديدين ‪ ،‬وكائن أخر‬


‫يسمى باني األوامر ‪. CommandBuilder‬‬

‫مالحظة ‪ :‬الكائن ‪ CommandBuilder‬يقوم ببناء األوامر الخاصة بتحديث‬


‫البيانات من تعديل وإضافة وحذف ‪ ،‬والتي يحتاجها الكائن ‪DataAdapter‬‬
‫في عملية التحديث ‪.‬‬

‫وخالل ذلك التصريح نقوم بربط كائن ‪ CommandBuilder‬بـالكائن‬


‫‪ ، DataAdapter‬ثم نقوم بإيقاف التحديث للبيانات الموجودة في الـ‬
‫‪ DataSet‬وقبول التحديثات الحالية من أجل حفظها ونقلها من الـ ‪DataSet‬‬
‫إلى مصدر البيانات ‪ ،‬ثم نختبر فيما إذا كان الكائن ‪ Dataset‬األصلي يحتوي‬
‫على أي تغييرات عليه ‪ ،‬وفي حالة وجود أي تغيير نستخدم الخاصية‬
‫‪ GetChanges‬لتأهيل الكائن الجديد ‪ ds_Changes‬بنفس بنية الكائن‬
‫األصلي ‪ ،‬و نستخدم أيضاً الخاصية ‪ GetChanges‬لتأهيل الكائن الجديد‬
‫‪ ds_subset‬بنفس بنية الكائن ‪ ، ds_Changes‬وجعله يحتوي فقط على‬
‫السجالت التي عدلت ‪ ،‬وقبل حفظ البيانات ونقلها من الـ ‪ DataSet‬إلى‬
‫مصدر البيانات باستخدام الخاصية ‪ Update‬التابعة للكائن‬
‫‪ ، MyDataAdapter‬نختبر الكائن الجديد ‪ ds_subset‬لنتأكد من احتوائه‬
‫على بيانات ‪ ،‬وفي حالة وجود بيانات نقوم بعملية النقل ‪ .‬ثم نكرر الجزء‬
‫األخير من الشرح بالنسبة لكل من اإلضافة والحذف ‪ ،‬وفي األخير‬
‫نستدعي الخاصية ‪ AcceptChanges‬إلعادة وضع الـ ‪ DataSet‬الذي تم‬
‫التعديل لبياناته ‪ ،‬جاعال ً االستدعاءات المستقبلية للخاصية ‪HasChanges‬‬
‫تعيد القيمة ‪..False‬‬

‫كود‬
‫‪   Dim ds_Changes As New DataSet‬‬
‫‪   Dim ds_subset As New DataSet‬‬
‫)‪   Dim MyCB As New OleDbCommandBuilder(MyDataAdapter‬‬
‫)(‪   Me.BindingContext(MyDS, "Information").EndCurrentEdit‬‬
‫‪   If MyDS.HasChanges Then‬‬
‫‪      ds_Changes = MyDS.GetChanges‬‬
‫)‪      ds_subset = ds_Changes.GetChanges(DataRowState.Modified‬‬
‫‪      If (Not (ds_subset) Is Nothing) Then‬‬
‫))‪MyDataAdapter.Update(ds_subset.Tables(0‬‬
‫)‪      ds_subset = ds_Changes.GetChanges(DataRowState.Added‬‬
‫‪      If (Not (ds_subset) Is Nothing) Then‬‬
‫))‪MyDataAdapter.Update(ds_subset.Tables(0‬‬
‫)‪      ds_subset = ds_Changes.GetChanges(DataRowState.Deleted‬‬
‫‪      If (Not (ds_subset) Is Nothing) Then‬‬
‫))‪MyDataAdapter.Update(ds_subset.Tables(0‬‬
‫)(‪      MyDS.AcceptChanges‬‬
‫‪End If‬‬

‫الخاصية ‪: HasErrors‬‬
‫نفرض أن مستخدماً أدخل القيمة " ‪ " ADO‬في حقل من النوع ‪Integer‬‬
‫عندئذ يمكن للبرنامج أن ُيع ِل ّم هذا الحقل في هذا السجل بالذات برسالة‬
‫خطأ ‪ .‬الحقاً وقبل أن يقوم البرنامج بتحديث قاعدة البيانات ‪ ،‬فإنه يمكن‬
‫استخدام خاصية ‪ HasErrors‬الخاصة بـ ‪ DataSet‬لتحديد فيما إذا كانت‬
‫البيانات تملك أخطاء يتوجب على المستخدم إصالحها ‪.‬‬
‫ربما يرى البعض أنه من األفضل أن يخبر البرنامج المستخدم عن وجود‬
‫أخطاء في البيانات المدخلة مباشرة ‪ ،‬مثل استخدام األداة‬
‫‪ ، ErrorProvider‬ولكن أحياناً يكون من المفيد تأجيل معالجة األخطاء ‪.‬‬
‫كمثال ‪ :‬ربما كانت قيمة أحد الحقول تعتمد على قيمة حقل آخر لم تدخل‬
‫بعد ‪ ،‬في هذه الحالة فإن البرنامج يجب عليه االنتظار حتى تدخل كال‬
‫القيمتين ‪ ،‬قبل أن يقرر فيما إذا كانت هناك مشكلة ما ‪.‬‬
‫وتعيد هذه الخاصية القيمة ‪ true‬إذا كانت البيانات تملك أخطاء يتوجب على‬
‫المستخدم إصالحها ‪ ،‬وبالمقابل القيمة ‪ False‬إذا لم توجد أخطاء في‬
‫البيانات ‪.‬‬
‫مثال ‪:‬‬

‫كود‬
‫‪If MyDataSet.HasErrors = True Then‬‬
‫أدخل الكود الخاص بتصحيح الخطأ ' ‪  ‬‬
‫‪Else‬‬
‫أدخل الكود الخاص بحفظ البيانات المدخلة ' ‪  ‬‬
‫‪End If‬‬
‫الخاصية ‪: Tables‬‬
‫نصل من خالل هذه الخاصية إلى الجداول المخزنة في ‪ DataSet‬ومنها إلى‬
‫الصفوف واألعمدة الموجودة في هذه الجداول ‪ ،‬بل و إلى القيم المخزنة‬
‫في أي خلية من الخاليا الموجودة في الجدول ‪.‬‬
‫ويتم تحديد اسم الجدول الذي نريد أن نتعامل معه بواسطة هذه الخاصية‬
‫بطريقتين ‪ ،‬أوالهما هي أن نكتب اسم الجدول ( وهي األفضل للمزيد من‬
‫الوضوح ) كما في المثال التالي ‪..‬‬

‫كود‬
‫)"‪MyDS.Tables("Information‬‬

‫والطريقة الثانية هي أن نرمز للجدول من خالل رقمه داخل الكائن ‪DataSet‬‬


‫علماً أن رقم أول جدول داخل الكائن ‪ DataSet‬هو الرقم ‪ ، 0‬كما في المثال‬
‫التالي ‪..‬‬

‫كود‬
‫)‪MyDS.Tables(0‬‬

‫ويمكن أيضاً التحكم في صفوف الجدول المحدد من خالل الخاصية ‪Rows‬‬


‫التابعة له ‪ ،‬والخاصية ‪ Item‬التابعة لها ‪ ،‬وكل ذلك يتم بشكل مباشر بدون‬
‫كائنات خدمية ‪.‬‬
‫وسوف أقوم بطرح مجموعة من األمثلة ‪ ،‬لعلها تعين على االستيعاب‬
‫بشكل أفضل ‪..‬‬

‫مالحظة ‪ :‬ما سوف يتم شرحه في السطور التالية من باب العلم‬


‫بالشيء ‪ ،‬وإال فإن الكائنات الخدمية (‪ BindingManagerBase‬أو‬
‫‪ CurrencyManager‬أو ‪ ) BindingSource‬تغنينا عن ذلك وبشكل مختصر‬
‫جداً وجميل ‪.‬‬

‫لمعرفة عدد سجالت أحد الجداول نقوم بكتابة ما يلي ‪..‬‬

‫كود‬
‫‪MyDS.Tables("Information").Rows.Count‬‬

‫لمعرفة القيمة المخزنة في الخلية الموجودة في الصف األول والعمود األول‬


‫‪ ،‬وعرضها في مربع رسالة ‪ ،‬نقوم بكتابة ما يلي ‪..‬‬

‫كود‬
‫))‪MsgBox(MyDS.Tables("Information").Rows(0).Item(0‬‬

‫كما نستطيع أعادة كتابة الكود السابق ‪ ،‬بحيث نحدد فيه عمود محدد ‪،‬‬
‫بكتابة ما يلي ‪..‬‬
‫كود‬
‫))"‪MsgBox(MyDS.Tables("Information").Rows(0).Item("Name‬‬

‫لتغير قيمة الخلية الموجودة في الصف األول والعمود المسمى ‪، Name‬‬


‫نقوم بكتابة ما يلي ‪..‬‬

‫كود‬
‫"‪MyDS.Tables("Information").Rows(0).Item("Name")="Yaser‬‬

‫لحذف السجل ( الصف ) األول من الجدول ‪ ،‬نقوم بكتابة ما يلي ‪..‬‬

‫كود‬
‫)(‪MyDS.Tables("Information").Rows(0).Delete‬‬

‫للتراجع عن التغيرات التي قمنا بها من تعديل وحذف ‪ ،‬نقوم بكتابة ما‬
‫يلي ‪..‬‬

‫كود‬
‫)(‪MyDS.Tables("Information").Rows(0).RejectChanges‬‬

‫إلضافة سجل جديد ‪ ،‬نخزن البيانات التي نريدها في مصفوفة ‪ ،‬ونسندها‬


‫للخاصية ‪ Add‬كما في الكود التالي ‪..‬‬

‫كود‬
‫}"‪   Dim MyData() As String = {"Yaser", "32", "Gizan‬‬
‫)‪MyDS.Tables("Information").Rows.Add(MyData‬‬

‫ويمكن تطوير الكود السابق باستخدام الكائن ‪ ، DataRow‬وتأهيله بصف‬


‫جديد في الجدول المحدد ‪ ،‬وإدخال القيم فيه من خالل الخاصية ‪ ، Item‬ثم‬
‫نسند الكائن ‪ DataRow‬إلى للخاصية ‪ Add‬التابعة لصفوف الجدول ‪ ،‬كما‬
‫في الكود التالي ‪..‬‬

‫كود‬
‫‪   Dim MyDR As DataRow = MyDS.Tables("Information").NewRow‬‬
‫"‪   MyDR.Item("name") = "yaser mohamed‬‬
‫"‪   MyDR.Item("age") = "32‬‬
‫"‪   MyDR.Item("City") = "Gizan‬‬
‫)‪MyDS.Tables("Information").Rows.Add(mydr‬‬

‫سنكتفي بذلك القدر ‪ ،‬وننتقل إلى التنقل بين السجالت ‪..‬‬

‫لمعرفة رقم موقع السجل الحالي وعرضه في مربع رسالة ‪ ،‬نكتب ما‬
‫يلي ‪..‬‬

‫كود‬
‫)‪MsgBox(Me.BindingContext(MyDS, "Information").Position‬‬

‫ولمعرفة عدد السجالت الموجودة في الجدول الحالي وعرضها في مربع‬


‫رسالة ‪ ،‬نكتب ما يلي ‪..‬‬

‫كود‬
‫)‪MsgBox(Me.BindingContext(MyDS, "Information").Count‬‬

‫ولالنتقال للسجل األول في الجدول نكتب ما يلي ‪..‬‬

‫كود‬
‫‪Me.BindingContext(MyDS, "Information").Position = 0‬‬

‫ولالنتقال للسجل األخير في الجدول نكتب ما يلي ‪..‬‬

‫كود‬
‫‪   Me.BindingContext(MyDS, "Information").Position = Me.BindingContext(MyDS,‬‬
‫‪"Information").Count -1‬‬

‫ولالنتقال للسجل التالي في الجدول نكتب ما يلي ‪..‬‬

‫كود‬
‫‪   Dim MyPosition As Integer = Me.BindingContext(MyDS, "Information").Position‬‬
‫‪Me.BindingContext(MyDS, "Information").Position = MyPosition +1‬‬

‫ولالنتقال للسجل السابق في الجدول نكتب ما يلي ‪..‬‬

‫كود‬
‫‪   Dim MyPosition As Integer = Me.BindingContext(MyDS, "Information").Position‬‬
‫‪Me.BindingContext(MyDS, "Information").Position = MyPosition -1‬‬

‫** هنا ينتهي حديثنا اليوم ‪ ،‬فما أصبنا من صواب فمن هللا وتوفيقه ‪ ،‬وما‬
‫أصبنا من خطأ فمن أنفسنا والشيطان ‪ ،‬لذلك من وجد أي أخطاء‬
‫فليصححها لنا مشكوراً ‪..‬‬
‫ولنأخذ قسطاً من الراحة ‪ ،‬لكي نستوعب هذه الجرعة من المعلومات‬
‫ونصحح أخطائها إن وجدت ‪ ،‬ونعلق على الغير مفهوم منها ‪ ،‬و نكمل فيما‬
‫بعد شرح ما تبقى من الدرس ‪ ،‬علماً أن موضوع درسنا القادم إن شاء هللا‬
‫هو الكائن ‪.. DataAdapter‬‬
‫((سبحانك اللهم وبحمدك‪ ،‬أشهد أن ال إله إال أنت‪ ،‬أستغفرك وأتوب إليك ))‬

‫‪.. DataSet‬‬

‫الخاصية ‪ ( Merge‬الدمج ) ‪:‬‬

‫تقوم هذه الخاصية بدمج أو إضافة البيانات إلى الكائن ‪ DataSet‬من كائنات‬
‫أخرى يمكن أن تكون مجمعة البيانات ‪ DataSet‬أو جدول ‪ DataTable‬أو‬
‫مد َمج عاد ًة بنية مشابهة‬
‫مصفوفة البيانات ‪ ، DataRow‬ويملك الكائن ال ُ‬
‫للكائن ‪ DataSet‬األصلية ‪ ،‬كمثال ‪ :‬إذا كان الكائن الجديد هو عبارة عن‬
‫‪ DataSet‬أخرى ‪ ،‬فإنه من الممكن أن يملك الجداول نفسها التي في‬
‫‪ DataSet‬األولى ( األصلية ) ‪.‬‬

‫وطريقة استخدامها هي كالتالي ‪:‬‬


‫‪ -1‬عبارة عن دمج ‪ DataSet‬فرعية داخل ‪ DataSet‬أصلية ‪ ،‬ومثال ذلك ‪..‬‬

‫كود‬
‫‪Dim MyDS As DataSet‬‬
‫‪Dim SubSet_DS As DataSet‬‬

‫)‪MyDS.Merge(dataSet‬‬

‫‪ -2‬عبارة عن دمج ‪ DataTable‬داخل ‪ DataSet‬أصلية ‪ ،‬ومثال ذلك ‪..‬‬

‫كود‬
‫‪Dim MyDS As DataSet‬‬
‫‪Dim MyTable As DataTable‬‬

‫)‪MyDS.Merge(MyTable‬‬

‫‪ -3‬عبارة عن دمج ‪ DataRow‬داخل ‪ DataSet‬أصلية ‪ ،‬ومثال ذلك ‪..‬‬

‫كود‬
‫‪Dim MyDS As DataSet‬‬
‫)(‪Dim Rows As DataRow‬‬

‫)‪MyDS.Merge(Rows‬‬
‫ومن المعلوم أن الكائن ‪ DataSet‬المولَّدة بواسطة الخاصية ‪GetChange‬‬
‫متوافقة مع ‪ DataSet‬األصلية ( الحتوائها على نفس التكوين و البنية ) ‪.‬‬
‫حيث يمكن للبرنامج على سبيل المثال استخدام الخاصية ‪GetChange‬‬
‫للحصول على كائن ‪ DataSet‬فرعي والمحتوي فقط على التغييرات ( أو‬
‫اإلضافات أو المحذوفات كما ذكرنا في الدروس السابقة ) ‪ ،‬ويمكنه بعد ذلك‬
‫تثبيت التغييرات مع إمكانية تعديل بعض قيم البيانات ‪ ،‬ثم يحفظ التغييرات‬
‫في قاعدة البيانات ‪ .‬وبعد انتهاء البرنامج من تحديث قاعدة البيانات ‪ ،‬فإن‬
‫البرنامج يمكنه من خالل الخاصية ‪ Merge‬دمج التغييرات معيداً إياها إلى‬
‫‪ DataSet‬األصلية ‪ ،‬وبالتالي فإنها ستكون متزامنة مع قاعدة البيانات ‪.‬‬
‫ويفرض علينا ذلك طبيعة البرنامج الذي نتعامل معه ‪ ،‬وخصوصاً في حالة‬
‫احتوائه على تقيدات عالئقية أو تقيدات أمنية معينه يحتاجها المبرمج ‪،‬‬
‫ولكي تتضح الصورة فلنتأمل المثال التالي ( يحتاج هذا المثال إلى قراءة و‬
‫فهم الدروس السابقة ) ‪..‬‬

‫كود‬
‫‪Dim ds_Changes As New DataSet‬‬
‫‪   Dim ds_subset As New DataSet‬‬
‫)‪   Dim MyCB As New OleDbCommandBuilder(MyDataAdapter‬‬
‫)(‪   Me.BindingContext(MyDS, "Information").EndCurrentEdit‬‬
‫‪     If MyDS.HasChanges Then‬‬
‫‪         ds_Changes = MyDS.GetChanges‬‬
‫)‪         ds_subset = ds_Changes.GetChanges(DataRowState.Modified‬‬
‫‪         If (Not (ds_subset) Is Nothing) Then‬‬
‫))"‪             MyDA.Update(ds_subset.Tables("Information‬‬
‫)‪             MyDS.Merge(ds_subset‬‬
‫‪         End If‬‬
‫)‪         ds_subset = ds_Changes.GetChanges(DataRowState.Added‬‬
‫‪         If (Not (ds_subset) Is Nothing) Then‬‬
‫))"‪             MyDA.Update(ds_subset.Tables("Information‬‬
‫)‪             MyDS.Merge(ds_subset‬‬
‫‪         End If‬‬
‫)‪         ds_subset = ds_Changes.GetChanges(DataRowState.Deleted‬‬
‫‪         If (Not (ds_subset) Is Nothing) Then‬‬
‫))"‪             MyDA.Update(ds_subset.Tables("Information‬‬
‫)‪             MyDS.Merge(ds_subset‬‬
‫‪         End If‬‬
‫)(‪         MyDS.AcceptChanges‬‬
‫‪  End If‬‬

‫و من باب العلم بالشيء ‪ ،‬الخاصية ‪ Merge‬تأخذ وسيطين اختياريين غير‬


‫إلزاميين ‪ ،‬يخبرانها بكيفية الدمج ‪ ،‬الوسيط األول هو قيمة منطقية (‬
‫‪ ) Boolean‬وتحمل إما القيمة ‪ True‬أو القيمة ‪ ، False‬وتخبر الخاصية‬
‫‪ Merge‬فيما إذا يجب أن ُتحافظ على التغييرات في ‪ DataSet‬األصلي وذلك‬
‫من خالل اختيار القيمة ‪ ، True‬أم ال وذلك من خالل اختيار القيمة ‪. False‬‬
‫الوسيط الثاني ويحمل أربع قيم ‪ ،‬من خاللها يخبر الخاصية ‪ Merge‬ماذا‬
‫يتوجب عليها فعله عند فقدان جدول أو عمود ما في الـ ‪ DataSet‬الفرعية‬
‫أو في الـ ‪ DataSet‬األصلية ‪ ،‬حيث أنه عند استدعاء الخاصية ‪ Merge‬تقوم‬
‫بالمقارنة بين مخططين الـ ‪ DataSet‬األصلي والفرعي ‪ ،‬وذلك بسبب أنه‬
‫من المحتمل أن ذلك المخططين قد يختلفان بسبب فقدان جدول أو عمود‬
‫ما كما أسلفنا ‪.‬‬
‫و ملخص هذه القيم ‪ ،‬مع طريقة استخدامها ‪-:‬‬

‫‪ -1‬القيمة ‪: MissingSchemaAction.Add‬‬
‫تجعل الخاصية ‪ Merge‬تضيف الجدول أو العمود الجديد ‪.‬‬

‫كود‬
‫)‪MyDS.Merge(ds_subset, False, MissingSchemaAction.Add‬‬

‫‪ -2‬القيمة ‪: MissingSchemaAction.AddWithKey‬‬
‫تجعل الخصية ‪ Merge‬تضيف أعمدة جديدة ومعلومات عن المفتاح الرئيسي‬
‫عند الضرورة ‪.‬‬

‫كود‬
‫)‪MyDS.Merge(ds_subset, False, MissingSchemaAction.AddWithKey‬‬

‫‪ -3‬القيمة ‪: MissingSchemaAction.Error‬‬
‫تجعل الخاصية ‪ Merge‬تعلن عن أخطاء ‪ ،‬إذا لم تجد مطابقة البيانات الواردة‬
‫مع بنية الـ ‪ DataSet‬األصلية ‪.‬‬

‫كود‬
‫)‪MyDS.Merge(ds_subset, False,  MissingSchemaAction.Error‬‬

‫‪ -4‬القيمة ‪: MissingSchemaAction.Ignore‬‬
‫تجعل الخاصية ‪ Merge‬تتجاهل البيانات الجديدة إذا لم تتطابق مع الـ‬
‫‪ DataSet‬األصلية ‪.‬‬

‫كود‬
‫)‪MyDS.Merge(ds_subset, False, MissingSchemaAction.Ignore‬‬

‫الخاصية ‪: Clone‬‬
‫تنسخ هذه الخاصية نفس بنية الـ ‪ DataSet‬األصلية إلى الـ ‪DataSet‬‬
‫الجديدة ‪ ،‬بحيث تمتلك نفس الجداول والتقيدات ‪ ،‬وطريقة استخدامها ‪..‬‬

‫كود‬
‫‪Dim MyDS As New DataSet‬‬
‫‪   Dim ds_subset As New DataSet‬‬

‫‪ds_subset = MyDS.Clone‬‬

‫الخاصية ‪: Copy‬‬
‫تنسخ هذه الخاصية نفس بنية و بيانات الـ ‪ DataSet‬األصلية إلى الـ‬
‫‪ DataSet‬الجديدة ‪ ،‬بحيث تمتلك نفس الجداول والتقيدات والبيانات أيضاً ‪،‬‬
‫وبالتالي فإن ‪ Copy‬مشابهة لـ ‪ Clone‬ولكنها تتضمن البيانات ‪ ،‬وطريقة‬
‫استخدامها ‪..‬‬

‫كود‬
‫‪Dim MyDS As New DataSet‬‬
‫‪   Dim ds_subset As New DataSet‬‬

‫‪ds_subset = MyDS.Copy‬‬

‫((سبحانك اللهم وبحمدك‪ ،‬أشهد أن ال إله إال أنت‪ ،‬أستغفرك وأتوب إليك ))‬
‫‪-----------------------------------------------------------------------------------------------------------------‬‬
‫‪-‬‬

‫وبه أبدا وأستعين ‪ ،‬وأختم بالصالة والسالم على خاتم األنبياء والمرسلين‬
‫نبينا محمد وعلى آله وصحبه أجمعين ‪ ،‬أما بعد ‪..‬‬
‫=============‬
‫‪ - 3‬الكائن ‪: DataAdapter‬‬
‫في الدروس السابقة وضحنا العمل الذي يقوم به هذا الكائن ‪ ،‬حيث قلنا‬
‫حينها أنه " بمثابة وسيط بين مجموعة البيانات "‪ " DataSet‬ومصادر‬
‫البيانات "‪ ، "Data Sources‬حيث يقوم ‪ DataAdapter‬بعملية فتح االتصال‬
‫بقاعدة البيانات و استيراد وتصدير البيانات من و إلى مصدرها األساسي ‪،‬‬
‫وبعد تخزين البيانات في ‪ ، DataSet‬فإنها تصبح منفصلة عن مصدر‬
‫البيانات ‪ ،‬الن الكائن ‪ DataAdapter‬يقوم بشكل تلقائي أيضاً بقطع االتصال‬
‫بقاعدة البيانات " ‪ ،‬ولكن هذا ليس كل شيء ‪ ،‬ما زلنا نحتفظ بالجزء‬
‫األفضل لدرس اليوم ‪...‬‬
‫يستخدم هذا الكائن كائنان صديقان تعرفنا عليهما فيما سبق ‪ ،‬وهما ‪:‬‬
‫الكائن ‪ OleDbConnection‬إلدارة اتصاله بقاعدة البيانات ( من فتح االتصال و‬
‫غلق االتصال ) ‪ ،‬و الكائن ‪ OleDbCommand‬إلدارة استعالمات ‪ SQL‬البنيوية‬
‫ ولتجهيز هذا‬. ) ‫التي يجب تنفيذها ( من تحديد أو إضافة أو تعديل أو حذف‬
‫ فقط على المبرمج أن‬، ‫الكائن للعمل هناك أكثر من طريقة وجميعها جيده‬
، ‫ والكود التالي يبين كيف يتم إنشاء مالئم بيانات‬، ‫يختار ما يناسبه منها‬
‫ وكذلك‬، ‫ إلدارة اتصاله بقاعدة البيانات‬OleDbConnection ‫واستخدام الكائن‬
‫ والذي يقوم‬SQL ‫ إلدارة استعالم‬OleDbCommand ‫استخدام الكائن‬
‫بتحديد مجموعة من الحقول المتوفرة في إحدى جداول قاعدة البيانات‬
‫ وكذلك سوف نستخدم إحدى خصائص الكائن‬، ‫التي تم االتصال بها‬
، ‫ ولكنا لن نزعج أنفسنا بها هنا‬SelectCommand ‫ وهي‬DataAdapter
‫ فقط نحتاج إلى القليل من‬، ‫وسوف نشرحها بالتفصيل في وقت الحق‬
.. ‫التركيز لتأمل الكود التالي‬

‫كود‬
' Create the ConnectionString.
Dim selectConnection As OleDbConnection = New OleDbConnection _
    ("Provider=Microsoft.Jet.OLEDB.4.0;" _
    & "Data Source=|DataDirectory|\Note.mdb;" _
    & "User Id=admin;" _
    & "Jet OLEDB:Database Password=ado.net;")
' Create the SelectCommand.
Dim selectCommand As OleDbCommand = New OleDbCommand("SELECT *
FROM Information")        
Dim instance_adapter As New OleDbDataAdapter()
instance_adapter.SelectCommand = selectCommand
instance_adapter.SelectCommand.Connection = selectConnection

.. ‫ويمكن اختصار الكود السابق ليصبح كما يلي‬

‫كود‬
' Create the ConnectionString.
Dim selectConnection As OleDbConnection = New OleDbConnection _
    ("Provider=Microsoft.Jet.OLEDB.4.0;" _
    & "Data Source=|DataDirectory|\Note.mdb;" _
    & "User Id=admin;" _
    & "Jet OLEDB:Database Password=ado.net;")
' Create the SelectCommand.
Dim selectCommand As OleDbCommand = New OleDbCommand("SELECT *
FROM Information", _  
    selectConnection)
Dim instance_adapter As New OleDbDataAdapter()
instance_adapter.SelectCommand = selectCommand

.. ‫ويمكن اختصار الكود السبق أكثر من ذلك ليصبح كما يلي‬

‫كود‬
Dim selectConnection As OleDbConnection = New OleDbConnection _
    ("Provider=Microsoft.Jet.OLEDB.4.0;" _
    & "Data Source=|DataDirectory|\Note.mdb;" _
    & "User Id=admin;Jet OLEDB:Database Password=ado.net;")
Dim selectCommandText As String = "SELECT * FROM Information"
Dim instance_adapter As New OleDbDataAdapter(selectCommandText,
selectConnection)

.. ‫ويمكن اختصار الكود السبق أكثر من ذلك ليصبح كما يلي‬

‫كود‬
Dim instance_adapter As New OleDbDataAdapter("SELECT * FROM Information" _
, "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\Note.mdb;" _
& "User Id=admin;Jet OLEDB:Database Password=ado.net;")

-: ‫ ومن أهمها‬، ‫ بالعديد من الخصائص‬DataAdapter ‫ويتمتع هذا الكائن‬


: Fill ‫* الخاصية‬
‫حيث تنقل البيانات من قاعدة البيانات (مصدرها األساسي ) إلى‬
‫ والكود‬. ‫ وبعبارة أخرى القيام بعملية االستيراد‬، DataTable ‫أو‬DataSet
‫ واستخدام‬، ‫ وتجهيزه‬DataAdapter ‫التالي يبين كيف يتم إنشاء الكائن‬
، ‫ بالجدول المحدد في جملة االستعالم‬DataSet ‫ لتعبئة الـ‬Fill ‫الخاصية‬
‫ومن بعد ذلك نعرض مربع رسالة تخبرنا بعدد السجالت المتوفرة في‬
.. ‫ للتو‬DataSet ‫الجدول الذي تم تخزينه في‬

‫كود‬
Dim selectCommandText As String = "SELECT * FROM Information"
Dim selectConnection As OleDbConnection = New OleDbConnection _
    ("Provider=Microsoft.Jet.OLEDB.4.0;" _
    & "Data Source=|DataDirectory|\Note.mdb;" _
    & "User Id=admin;" _
    & "Jet OLEDB:Database Password=ado.net;")
Dim instance_adapter As New OleDbDataAdapter(selectCommandText,
selectConnection)
Dim MyDs As New DataSet
instance_adapter.Fill(MyDs)
MsgBox(MyDs.Tables(0).Rows.Count, MsgBoxStyle.Information)

، ) ‫ تأخذ الكثير من المتغيرات ( البارامترات‬Fill ‫في الحقيقة الخاصية‬


‫ ومن خالله نستطيع أن نضع اسم‬srcTable ‫ وهو‬، ‫سنتحدث عن أهمها‬
‫ وذلك من أجل الحصول على‬DataSet ‫للجدول الذي نريد تخزينه في الـ‬
" ‫ وبدون هذا المتغير سوف يأخذ هذا الجدول االسم‬، ‫اسم ذو معنى أوضح‬
‫ وفي مثالنا السابق نستطيع استبدال السطر التاسع بالكود‬، "Table
.. "Demo" ‫التالي إلعطاء الجدول اسم‬

‫كود‬
instance_adapter.Fill(ds, "Demo")
‫ولكي نتأكد من اسم الجدول الذي قمنا بتخزينه في الـ ‪ ، DataSet‬نكتب‬
‫الكود التالي لعرض مربع رسالة تخبرنا باسم الجدول ‪..‬‬

‫كود‬
‫)‪MsgBox(ds.Tables(0).TableName‬‬

‫مالحظة ‪ -:‬الصورة الكاملة للمتغيرات ( البارامترات ) التي تأخذها الخاصية‬


‫‪.. Fill‬‬

‫كود‬
‫‪instance_adapter.Fill(MyDataSet As DataSet, startRecord  As Integer,‬‬
‫)‪maxRecords As Integer, srcTable As String‬‬

‫حيث أن ‪:‬‬
‫‪ -1‬البارامتر ‪ MyDataSet‬يشير إلى الـ ‪ Dataset‬التي نريد تخزين الجدول‬
‫بها ‪.‬‬
‫‪ -2‬البارامتر ‪ startRecord‬يشير إلى رقم السجل الذي نريد أن نبدأ به في‬
‫تحميل البيانات ( يجب أن نعلم أن هذا الرقم يبدأ بالرقم صفر ويدل على‬
‫السجل األول ) من الجدول الذي نريد نسخ بياناته وتكوينه في الـ ‪Dataset‬‬
‫‪.‬‬
‫‪ -3‬البارامتر ‪ maxRecords‬يشير إلى رقم السجل الذي نريد أن ننتهي‬
‫عنده في تحميل البيانات من الجدول الذي نريد نسخ بياناته وتكوينه في الـ‬
‫‪. Dataset‬‬
‫‪ -4‬البارامتر ‪ srcTable‬يشير إلى االسم المقترح للجدول الذي نريد تكوينه‬
‫في الـ ‪. Dataset‬‬
‫مثال ‪-:‬‬
‫في المثال التالي لدينا جدول يحتوي على مجموعة من السجالت عددها‬
‫‪ 15‬سجل ‪ ،‬ونريد أن نخزن تكوين وبيانات هذا الجدول في الـ ‪، DataSet‬‬
‫ونريد أيضاً اختيار مجموعة محدده من السجالت ‪ ،‬على أن نبدأ من السجل‬
‫الخامس ( بالطبع ترتيبه داخل السجالت يحمل الرقم ‪ ) 4‬وننتهي إلى‬
‫السجل العاشر ( بالطبع ترتيبه داخل السجالت يحمل الرقم ‪ ، ) 9‬وبعد ذلك‬
‫نريد أن نعطي هذا الجدول االسم "‪ ، "Demo‬وفي األخير نعرض مربع‬
‫رسالة تخبرنا بعدد السجالت في الجدول الذي تم تخزينه للتو ‪ ،‬وقيمة‬
‫السجل األول في العمود األول من الجدول ‪..‬‬

‫كود‬
‫‪Dim selectCommandText As String = "SELECT * FROM Information order by‬‬
‫"‪name‬‬
‫_ ‪Dim selectConnection As OleDbConnection = New OleDbConnection‬‬
‫_ ";‪    ("Provider=Microsoft.Jet.OLEDB.4.0‬‬
‫_ ";‪    & "Data Source=|DataDirectory|\Note.mdb‬‬
‫_ ";‪    & "User Id=admin‬‬
‫)";‪    & "Jet OLEDB:Database Password=ado.net‬‬
‫‪Dim instance_adapter As New OleDbDataAdapter(selectCommandText,‬‬
‫)‪selectConnection‬‬
‫‪Dim ds As New DataSet‬‬
‫)"‪instance_adapter.Fill(ds, 4, 9, "Demo‬‬
‫‪Dim strMsg As String = String.Format("Count of Records In The {0} Table = {1}.‬‬
‫_ " ‪{2}The‬‬
‫_ ‪& "First Value In The second Column Is : {3} .",‬‬
‫_ ‪ds.Tables(0).TableName, ds.Tables(0).Rows.Count, vbNewLine,‬‬
‫)‪ds.Tables(0).Rows(0).Item(0‬‬
‫)‪MsgBox(strMsg, MsgBoxStyle.Information‬‬

‫(( يمكن تغيير األرقام ومشاهدة النتائج المختلفة ))‬

‫* الخاصية ‪: TableMappings‬‬
‫عندما يختار ‪ DataAdapter‬بياناته من قاعدة البيانات بواسطة جملة‬
‫االستعالم للمرة األولى ‪ ،‬فإنه يخزنها في الـ ‪ DataSet‬ضمن جدول يحمل‬
‫اسم افتراضي ‪ Table‬بشكل تلقائي ‪ ،‬وأي تحميل آخر للبيانات في الـ‬
‫‪ DataSet‬فإنها سوف تخزن ضمن جداول تحمل اسم ‪... ، table1 ، Table2‬‬
‫وهكذا ‪ ،‬و هذا االسم كما يبدو ال يدل على اسم ذو معنى واضح ‪،‬‬
‫ونستطيع من خالل ‪ TableMappings‬تحويله إلى اسم ذو معنى أوضح ‪،‬‬
‫ولعل التمثيل الذي في الصورة التالية يوضح الفكرة ‪...‬‬

‫ولكي نقوم بذلك نحتاج إلى تمرير متغيرين من نوع ‪ String‬للخاصية ‪Add‬‬
‫التابعة للخاصية ‪ ، TableMappings‬األول ‪ SourceTable‬وهو اسم الجدول‬
‫االفتراضي ‪ ،‬والثاني ‪ DataSetTable‬وهو االسم الذي نريده للجدول الذي‬
‫يجب أن يستقبل البيانات في الـ ‪. DataSet‬‬
‫ومثال على ذلك ‪:‬‬
‫الكود التالي يبين كيف يتم إنشاء الكائن ‪ DataAdapter‬وتجهيزه ‪،‬‬
‫واستخدام الخاصية ‪ TableMappings‬لتغيير اسم الجدول الذي يجب أن‬
‫يستقبل البيانات داخل الـ ‪ DataSet‬وفي النهاية نعرض مربع رسالة توضح‬
.. ‫اسم الجدول مع عدد سجالته‬

‫كود‬
Dim selectCommandText As String = "SELECT * FROM [Information]"
Dim selectConnection As OleDbConnection = New OleDbConnection _
    ("Provider=Microsoft.Jet.OLEDB.4.0;" _
    & "Data Source=|DataDirectory|\Note.mdb;" _
    & "User Id=admin;" _
    & "Jet OLEDB:Database Password=ado.net;")
Dim instance_adapter As New OleDbDataAdapter(selectCommandText,
selectConnection)
Dim ds As New DataSet
instance_adapter.TableMappings.Add("Table", "Info")
instance_adapter.Fill(ds)
Dim strMsg As String = String.Format("Count of Records In The {0} Table =
{1} .", _
    ds.Tables(0).TableName, ds.Tables(0).Rows.Count)
MsgBox(strMsg, MsgBoxStyle.Information)

‫ فإنه يغنينا عن‬، ‫ (( لو كان األمر مجرد تغير اسم الجدول‬: ‫مالحظة هامة‬
‫ مجرد كتابة االسم المقترح للجدول خالل استدعاء الخاصية‬، ‫كل هذا العناء‬
.. ‫ وهذه هي اإلعادة للكود الالزم لعمل مثل ذلك‬، ‫ كما ذكرنا سابق ُا‬Fill

‫كود‬
instance_adapter.Fill(ds, "Demo")

. )) ‫هذا رائي الشخصي‬

) ‫ليس ذلك فقط بل نستطيع كذلك تغيير أسماء الحقول ( األعمدة‬


‫ وذلك من خالل‬، ‫الموجودة دخل الجدول إلى أسماء ذو معنى أوضح‬
ً‫ وهي أيضا‬، TableMappings ‫ التابعة للخاصية‬ColumnMappings ‫الخاصية‬
‫ األول‬، ‫ التابعة لها‬Add ‫ للخاصية‬String ‫تحتاج إلى تمرير متغيرين من نوع‬
‫ والثاني‬، ‫ وهو اسم العمود األساسي في قاعدة البيانات‬SourceColumn
‫ وهو االسم الجديد الذي نريده للعمود في الجدول‬DataSetColumn
. DataSet ‫المخزن في الـ‬
... ‫ولعل التمثيل الذي في الصورة التالية يوضح الفكرة‬
: ‫ومثال على ذلك‬
، ‫ وتجهيزه‬DataAdapter ‫الكود التالي يبين كيف يتم إنشاء الكائن‬
‫ لتغيير اسم الجدول الذي يجب أن‬TableMappings ‫واستخدام الخاصية‬
‫ ويلي ذلك نقوم باستخدام الخاصية‬، DataSet ‫يستقبل البيانات داخل الـ‬
‫ لتغيير أسماء بعض الحقول ( علماً أننا سوف نشير‬ColumnMappings
‫) وذلك كي نخبر هذه الخاصية أننا نريد‬0( ‫ بالرمز‬Table ‫للجدول االفتراضي‬
‫ وفي النهاية نعرض مربع رسالة توضح اسم‬،) ‫أن يتم إجراء التعديالت عليه‬
.. ‫ واسم العمود الثاني في الجدول‬، ‫الجدول مع عدد سجالته‬

‫كود‬
Dim selectCommandText As String = "SELECT * FROM [Information]"
Dim selectConnection As OleDbConnection = New OleDbConnection _
    ("Provider=Microsoft.Jet.OLEDB.4.0;" _
    & "Data Source=|DataDirectory|\Note.mdb;" _
    & "User Id=admin;" _
    & "Jet OLEDB:Database Password=ado.net;")
Dim instance_adapter As New OleDbDataAdapter(selectCommandText,
selectConnection)
Dim ds As New DataSet
instance_adapter.TableMappings.Add("Table", "Info")
instance_adapter.TableMappings(0).ColumnMappings.Add("Name", "First Name")
instance_adapter.TableMappings(0).ColumnMappings.Add("ID", "InfoID")
instance_adapter.Fill(ds)
Dim strMsg As String = String.Format("Count of Records In The {0} Table =
{1} .{2}The  
    name Of the second Column Is It : {3} ."), _
    ds.Tables(0).TableName, ds.Tables(0).Rows.Count, vbNewLine,    
    ds.Tables(0).Columns(0).ColumnName )
MsgBox(strMsg, MsgBoxStyle.Information)

‫ فإنه يغنينا عن‬، ‫ (( لو كان األمر مجرد تغير أسماء األعمدة‬: ‫مالحظة هامة‬
‫ وهذا هو الكود‬، ‫ في جملة االستعالم‬AS ‫ مجرد كتابة عبارة‬، ‫كل هذا العناء‬
.. ‫الالزم لعمل مثل ذلك‬

‫كود‬
Dim selectCommandText As String = "SELECT ID AS [Info ID], Name AS [First
Name] FROM [Information]"

. )) ‫هذا رائي الشخصي‬

‫ مثاال ً على ذلك لو أننا أردنا تحميل البيانات من‬، ‫وهناك استخدامات أخرى‬
‫ وللقيام بذلك علينا أن‬، ‫ واحد‬DataAdapter ‫جدولين مختلفين من خالل‬
: ‫نتأمل المثال التالي حيث يقوم بتوضيح الفكرة‬
‫ واستخدام الخاصية‬، ‫ وتجهيزه‬DataAdapter ‫في البداية يتم إنشاء الكائن‬
‫ ) الذي‬Info ‫ لتغيير اسم الجدول األول ( وليكن أسمة‬TableMappings
‫ ويلي ذلك نقوم باستخدام‬، DataSet ‫يجب أن يستقبل البيانات داخل الـ‬
‫ لتغيير أسماء بعض الحقول ( علماً أننا سوف‬ColumnMappings ‫الخاصية‬
‫) وذلك كي نخبر هذه الخاصية أننا‬0( ‫ بالرمز‬Table ‫نشير للجدول االفتراضي‬
Fill ‫ وبعد ذلك نستدعي الخاصية‬،) ‫نريد أن يتم إجراء التعديالت عليه‬
TableMappings ‫ الخاصة‬DataSetTable ‫ ثم نغ ِي ّر القيمة‬، ‫لتحميل البيانات‬
‫ ( كل ذلك يتم‬Note ‫ والذي سوف نعطيه االسم‬، ‫إلى اسم الجدول الثاني‬
‫ وبعد ذلك نكتب‬، ‫ ونقوم بتغيير اسم أحد حقوله‬، ) DataSet ‫داخل الـ‬
‫جملة االستعالم التي نريدها أن تحظر لنا بيانات الجدول الثاني من مصدر‬
‫ فإن البيانات‬Fill ‫ وعندما نستدعي مرة أخرى الخاصية‬، ‫قاعدة البيانات‬
‫ وبذلك نكون قد‬، Note ‫الجديدة ستذهب مباشرة إلى الجدول المسمى‬
‫ وفي نهاية المطاف‬، ‫ واحد‬DataAdapter ‫حصلنا على جدولين من خالل‬
‫ نعرض مربع رسالة تخبرنا ببعض‬، ‫كي نتأكد من صحة التغييرات والمدخالت‬
.. ‫التفاصيل المتعلقة بالجدولين‬

‫كود‬
Dim selectCommandText As String = "SELECT * FROM [Information]"
Dim selectConnection As OleDbConnection = New OleDbConnection _
    ("Provider=Microsoft.Jet.OLEDB.4.0;" _
    & "Data Source=|DataDirectory|\Note.mdb;" _
    & "User Id=admin;" _
    & "Jet OLEDB:Database Password=ado.net;")
Dim instance_adapter As New OleDbDataAdapter(selectCommandText,
selectConnection)
Dim ds As New DataSet
instance_adapter.TableMappings.Add("Table", "Info")
instance_adapter.TableMappings(0).ColumnMappings.Add("Name", "FirstName")
instance_adapter.TableMappings(0).ColumnMappings.Add("ID", "InfoID")
instance_adapter.Fill(ds)
instance_adapter.TableMappings(0).DataSetTable = "Note"
instance_adapter.TableMappings(0).ColumnMappings.Add("Note", "MyNote")
instance_adapter.SelectCommand.CommandText = "SELECT * FROM [Note]"
instance_adapter.Fill(ds)
‫= ‪Dim strMsg As String = String.Format("Count of Records In The {0} Table‬‬
‫_ "‪{1} .‬‬
‫_ "‪    & "{2} The name Of the second Column Is It : {3} .‬‬
‫_ "‪    & "{4}Count of Records In The {5} Table = {6} .‬‬
‫_ ‪    & "{7} The name Of the second Column Is It : {8} .",‬‬
‫_ ‪    ds.Tables(0).TableName, ds.Tables(0).Rows.Count, vbNewLine,‬‬
‫_ ‪    ds.Tables(0).Columns(0).ColumnName,‬‬
‫_ ‪    vbNewLine, ds.Tables(1).TableName, ds.Tables(1).Rows.Count, vbNewLine,‬‬
‫)‪    ds.Tables(1).Columns(1).ColumnName‬‬
‫)‪MsgBox(strMsg, MsgBoxStyle.Information‬‬

‫* الخاصية ‪: FillSchema‬‬
‫تجعل هذه الخاصية الخاصة بالكائن ‪ DataAdapter‬يحمل بنية البيانات‬
‫الموجودة في الجدول المختار إلى الكائن ‪ DataSet‬بدون تحميل أية‬
‫بيانات ‪ ،‬وبعبارة أخرى هذه الخاصية مشابهة للخاصية ‪ Fill‬ولكنها ال تنسخ‬
‫البيانات معها ‪ .‬ولها ثالثة متغيرات ( بارامترات ) ‪:‬‬
‫‪ -‬المتغير األول هو الـ ‪.DataSet‬‬
‫‪ -‬المتغير الثاني هو ‪ SchemaType‬ومن خالله نخبر هذه الخاصية فيما إذا‬
‫كان يتوجب عليها استخدام ‪ TableMappings‬الخاص بالكائن ‪DataAdapter‬‬
‫أم ال ‪ ،‬و لهذا المتغير قيمتين تتحكم في ذلك األولى ‪ ( Mapped‬ينصح في‬
‫استخدامه في الغالب ) وهي تجعل ‪ DataAdapter‬يستعمل البنية التي‬
‫حددت له من قبل ‪ . TableMappings‬والقيمة الثانية هي ‪ Source‬وهي‬
‫تجعل ‪ DataAdapter‬يتجاهل البنية التي حددت له من قبل‬
‫‪ TableMappings‬وينشئ بنية تطابق بنية قاعدة البيانات ‪.‬‬
‫‪ -‬المتغير الثالث إختياري وهو ‪ srcTable‬يشير إلى االسم المقترح للجدول‬
‫الذي نريد نسخ تكوينه في الـ ‪ Dataset‬بغض النظر عن قيمة‬
‫‪. SchemaType‬‬
‫ومثال على ذلك ‪:‬‬
‫الكود التالي يبين كيف يتم إنشاء الكائن ‪ DataAdapter‬وتجهيزه ‪،‬‬
‫واستخدام الخاصية ‪ FillSchema‬لتعبئة الـ ‪ DataSet‬بالجدول المحدد في‬
‫جملة االستعالم ) بدون تحميل أي بيانات ) ‪ ،‬وسوف نستخدم المتغير‬
‫‪ SchemaType.Mapped‬لكي نجعل ‪ DataAdapter‬يستعمل البنية التي‬
‫حددت له من قبل ‪ ، TableMappings‬ومن بعد ذلك نعرض مربع رسالة‬
‫تخبرنا بعدد السجالت المتوفرة في الجدول الذي تم تخزينه في ‪DataSet‬‬
‫للتو وذلك للتأكد من أنها تساوي صفراً ‪..‬‬

‫كود‬
‫"‪Dim selectCommandText As String = "SELECT * FROM Information‬‬
‫_ ‪Dim selectConnection As OleDbConnection = New OleDbConnection‬‬
‫_ ";‪    ("Provider=Microsoft.Jet.OLEDB.4.0‬‬
‫_ ";‪    & "Data Source=|DataDirectory|\Note.mdb‬‬
‫_ ";‪    & "User Id=admin‬‬
‫)";‪    & "Jet OLEDB:Database Password=ado.net‬‬
‫‪Dim instance_adapter As New OleDbDataAdapter(selectCommandText,‬‬
‫)‪selectConnection‬‬
‫‪Dim ds As New DataSet‬‬
‫)"‪instance_adapter.TableMappings.Add("Table", "Demo‬‬
‫)"‪instance_adapter.TableMappings(0).ColumnMappings.Add("Name", "FirstName‬‬
‫)‪instance_adapter.FillSchema(ds, SchemaType.Mapped‬‬
‫= ‪Dim strMsg As String = String.Format("Count of Records In The {0} Table‬‬
‫_ ‪{1} .",‬‬
‫)‪ds.Tables(0).TableName, ds.Tables(0).Rows.Count‬‬
‫)‪MsgBox(strMsg, MsgBoxStyle.Information‬‬

‫ولو أردنا من الخاصية ‪ FillSchema‬أن تجعل ‪ DataAdapter‬يستعمل البنية‬


‫التي حددت له من قبل ‪ ، TableMappings‬مع إعطاء الجدول اسم‬
‫مختلف ‪ ،‬نستبدل السطر رقم ‪ 11‬بالكود التالي ‪..‬‬

‫كود‬
‫)"‪instance_adapter.FillSchema(ds, SchemaType.Mapped , "Info‬‬

‫ولو أردنا من الخاصية ‪ FillSchema‬أن تتجاهل البنية التي حددت له من‬


‫قبل ‪ TableMappings‬وتنشئ بنية تطابق بنية قاعدة البيانات ‪ ،‬نستبدل‬
‫السطر رقم ‪ 11‬بالكود التالي ‪..‬‬

‫كود‬
‫)‪instance_adapter.FillSchema(ds, SchemaType.Source‬‬

‫في هذه الحالة سوف يصبح اسم الجدول داخل الـ ‪ DataSet‬االسم‬
‫االفتراضي ‪. Table‬‬

‫ولو أردنا من الخاصية ‪ FillSchema‬أن تتجاهل البنية التي حددت له من‬


‫قبل ‪ TableMappings‬وتنشئ بنية تطابق بنية قاعدة البيانات ‪ ،‬مع إعطاء‬
‫الجدول اسم ذو معنى أوضح ‪ ،‬نستبدل السطر رقم ‪ 11‬بالكود التالي ‪..‬‬

‫كود‬
‫)"‪instance_adapter.FillSchema(ds, SchemaType.Source , "Demo‬‬

‫** هنا ينتهي حديثنا اليوم ‪ ،‬فما أصبنا من صواب فمن هللا وتوفيقه ‪ ،‬وما‬
‫أصبنا من خطأ فمن أنفسنا والشيطان ‪ ،‬لذلك من وجد أي أخطاء‬
‫فليصححها لنا ‪ ،‬أو يخبرنا به كي نصححه ‪ ،‬مشكوراً ‪..‬‬
‫ولنأخذ قسطاً من الراحة ‪ ،‬لكي نستوعب هذه الجرعة من المعلومات‬
‫ونصحح أخطائها إن وجدت ‪ ،‬ونعلق على الغير مفهوم منها ‪ ،‬و نكمل فيما‬
‫بعد شرح ما تبقى من الدرس ‪ ،‬ونظراً ألن الحديث عن الكائن‬
‫‪ DataAdapter‬طويل و ممتع فلقد قمنا للتو بشرج جزء ‪ ،‬وفي موضوع‬
‫درسنا القادم إن شاء هللا سوف نكمل الجزء اآلخر وهو الحديث عن‬
‫الخاصية ‪ UpDate‬وما يلحق بها ‪..‬‬

‫((سبحانك اللهم وبحمدك‪ ،‬أشهد أن ال إله إال أنت‪ ،‬أستغفرك وأتوب إليك ))‬

‫=============‬

‫الخاصية ‪: UpDate‬‬
‫تجعل هذه الخاصية الكائن ‪ DataAdapter‬ينسخ التغييرات بشكل عكسي‬
‫من الكائن ‪ DataSet‬إلى مصدر قاعدة البيانات ‪ ،‬وذلك من خالل تنفيذ‬
‫األوامر ‪ InsertCommand ، UpDateCommand ، DeleteCommand‬وذلك‬
‫لكل أسطر ‪ DataSet‬ألتي قد أدخلت أو عدلت أو حذفت ‪ .‬و سوف نقوم‬
‫بشرح كل واحد من هذه األوامر على حده ونبين كيف يعمل ‪ ،‬علماً أنه‬
‫يمكننا استخدام باني األوامر ‪ CommandBuilder‬ليقوم هو ببناء األوامر‬
‫الالزمة والتي سبق ذكرها نيابة عنا ‪ ،‬وهو أيضاً سوف نقوم بشرحه‬
‫بالتفصيل ‪.‬‬
‫كما نعلم ربما يحتوي الـ ‪ Dataset‬إلى أكثر من جدول ‪ ،‬لذلك للقيام بعملية‬
‫التحديث نحتاج إلى تحديد الجدول الذي نرغب في تحديثه كما في‬
‫المثال ‪..‬‬

‫كود‬
‫)"‪instance_adapter.Update(ds, "Information‬‬

‫أو الكود التالي ‪..‬‬

‫كود‬
‫))"‪instance_adapter.Update(ds.Tables("Information‬‬

‫أو الكود التالي في حالة معرفتنا بالرقم ألفهرسي للجدول المخزن في الـ‬
‫‪.. DataSet‬‬

‫كود‬
‫))‪instance_adapter.Update(ds.Tables(0‬‬

‫مالحظة هامة ‪:‬‬


‫ لتغيير االسم لجدول أو ألعمدة‬TableMappings ‫إذا استخدمت الخاصية‬
‫ فكن متأكداً من استخدام نفس التحويالت‬، ‫جدول ما عند تحميل البيانات‬
. ‫عند القيام بعملية التحديث‬

: InsertCommand ‫ األمر‬-
‫ و‬DataAdapte ‫ أحد خصائص الكائن‬InsertCommand ‫يعد أمر اإلضافة‬
‫ والطريقة‬، ‫عمله األساسي هو إضافة سجل جديد إلى سجالت الجدول‬
‫التي يعمل بها مشابهة تماماً لكائن األمر الذي قد تحدثنا عنه في الوضع‬
‫ حيث أنه في البداية سوف نقوم‬، ‫ والمثال التالي موضح بالشرح‬، ‫المتصل‬
‫ و نقوم بتحديث‬، ‫ بالبيانات‬DataSet ‫ ثم نمأل الـ‬، ‫بتجهيز الكائنات للعمل‬
‫ بشكل‬DataSet ‫ ثم نقوم بنسخ التغييرات التي طرأت على الـ‬، ‫بياناته‬
‫ من خال الكائن‬، ‫ إلى مصدر قاعدة البيانات‬DataSet ‫عكسي من الكائن‬
‫ وفي األخير‬، ‫ التابع له‬InsertCommand ‫ وكائن األمر‬DataAdapter
‫ فلنلقي نظرة‬، ‫ لتولي مسؤلية عملية النسخ‬UpDate ‫نستخدم الخاصية‬
.. ‫متفحصة على الكود‬

‫كود‬
1 Dim selectCommandText As String = "SELECT * FROM Information;"
2 Dim selectConnection As OleDbConnection = New OleDbConnection _
3     ("Provider=Microsoft.Jet.OLEDB.4.0;" _
4     & "Data Source=|DataDirectory|\Note.mdb;" _
5     & "User Id=admin;" _
6     & "Jet OLEDB:Database Password=ado.net;")

7 Dim instance_adapter As New OleDbDataAdapter(selectCommandText,


selectConnection)
8 Dim ds As New DataSet

9 instance_adapter.Fill(ds, "Information")

10 Dim MyDR As DataRow = ds.Tables("Information").NewRow


11 MyDR.Item("Name") = "Yaser Mohamed"
12 MyDR.Item("Age") = "32"
13 MyDR.Item("Phone") = "0123456789"
14 MyDR.Item("Email") = "Yaser1395@hotmail.com"
15 ds.Tables("Information").Rows.Add(MyDR)

16 Dim InsertCommand As New OleDbCommand


17 InsertCommand.Connection = selectConnection
18 InsertCommand.CommandText = "INSERT INTO Information ([Name],[age],
[phone],[email]) VALUES (?,?,?,?)"
19 InsertCommand.CommandType = CommandType.Text
20 InsertCommand = InsertCommand
21 InsertCommand.Parameters.AddWithValue("?", "Yaser Mohamed")
22 InsertCommand.Parameters.AddWithValue("?", "32")
23 InsertCommand.Parameters.AddWithValue("?", "0123456789")
24 InsertCommand.Parameters.AddWithValue("?", "Yaser1395@hotmail.com")

25 instance_adapter.InsertCommand = InsertCommand
‫)(‪26 Me.BindingContext(ds, "Information").EndCurrentEdit‬‬
‫)"‪27 instance_adapter.Update(ds, "Information‬‬

‫)‪28 MsgBox("Update Successfully...", MsgBoxStyle.Information‬‬

‫شرح الكود السابق متزامن مع أرقام األسطر ‪:‬‬


‫‪ -1‬التصريح عن متغير لإلستعالم عن الجدول المخزن في مصدر البيانات (‬
‫‪. ) Data Sources‬‬
‫‪ -2‬التصريح عن متغير لكائن اإلتصال بقدعدة البيانات ‪.‬‬
‫‪ -7‬التصريح عن متغير للكائن ‪ ( DataAdapter‬وهو الوسيط بين الكائن‬
‫‪ DataSet‬و مصدر البيانات ) و تجهيزه ‪.‬‬
‫‪ -8‬التصريح عن متغير الكائن ‪. DataSet‬‬
‫‪ -9‬استيراد البيانات من مصدر البيانات ‪ ،‬وتعبئتها في جدول محدد ضمن‬
‫الكائن ‪. DataSet‬‬
‫‪ -10‬الكود الالزم إلضافة سجل جديد إلى سجالت الجدول المخزن في‬
‫الكائن ‪ ، DataSet‬حيث يبدأ بالتصريح عن متغير كائن صف ‪ ،‬وتجهيزه من‬
‫خالل ربطه بالجدول الموجود في الكائن ‪ ، DataSet‬ثم استدعاء الخاصية‬
‫صف جديد ‪.‬‬
‫‪ -11‬بعد ذلك نقوم بإسناد البيانات المحدثه إلى الحقول الخاصة بالجدول‬
‫ويتكرر ذلك في كل من االسطر رقم ( ‪ 11‬إلى ‪. ) 14‬‬
‫‪ -15‬نقوم بإضافة كائن الصف إلى صفوف الجدول الموجود في الكائن‬
‫‪ ،DataSet‬وبذلك نكون قد حدثنا الجدول المخزن في الكائن ‪. DataSet‬‬
‫‪ -16‬ولكي نقوم بتصدير البيانات الجديده من الكائن ‪ DataSet‬إلى مصدر‬
‫البيانات ‪ ،‬نحتاج إلى اعداد وتجهيز كائن األمر الخاص بإضافة صف جديد ‪،‬‬
‫وذلك من خالل إنشاء كائن أمر وإعداده كما كنا نعمل في الوضع المتصل ‪،‬‬
‫وذلك نجده في كل من االسطر رقم ( ‪ 16‬إلى ‪. ) 24‬‬
‫‪ -25‬بعد ذلك نقوم بإسناد كائن األمر إلى الخاصية الخاصة بأمر اإلضافة في‬
‫الكائن ‪. DataAdapter‬‬
‫‪ -26‬وللقيام بعملية التحديث النهائية لمصدر البيانات نحتاج إلى إيقاف‬
‫عملية التحديث على الجدول المطلوب ضمن الكائن ‪ ، DataSet‬وذلك‬
‫حفاظاً على تزامن البيانات ‪.‬‬
‫‪ -27‬هنا نقوم بعملية التحديث بعد تحديد اسم الجدول الذي نرغب في‬
‫تحديثه ‪.‬‬
‫‪ -28‬مربع رسالة تخبرنا بنجاح العملية ‪.‬‬

‫ويكمن كتابة الكود السابق بشكل أخر وسوف يؤدي نفس النتيجة ‪،‬‬
‫فلنتأمل الكود التالي ‪..‬‬

‫كود‬
1 Dim selectCommandText As String = "SELECT * FROM Information;"
2 Dim selectConnection As OleDbConnection = New OleDbConnection _
3     ("Provider=Microsoft.Jet.OLEDB.4.0;" _
4     & "Data Source=|DataDirectory|\Note.mdb;" _
5     & "User Id=admin;" _
6     & "Jet OLEDB:Database Password=ado.net;")

7 Dim instance_adapter As New OleDbDataAdapter(selectCommandText,


selectConnection)
8 Dim ds As New DataSet

9 instance_adapter.Fill(ds, "Information")

10 Dim MyDR As DataRow = ds.Tables("Information").NewRow


11 MyDR.Item("Name") = "Yaser Mohamed"
12 MyDR.Item("Age") = "32"
13 MyDR.Item("Phone") = "0123456789"
14 MyDR.Item("Email") = "Yaser1395@hotmail.com"
15 ds.Tables("Information").Rows.Add(MyDR)

16 instance_adapter.InsertCommand = New OleDbCommand("INSERT INTO


Information ([Name],[age],[phone],[email]) VALUES (?,?,?,?)" ,
selectConnection)

17 instance_adapter.InsertCommand.Parameters.Clear()
18 instance_adapter.InsertCommand.Parameters.AddWithValue("?", "Yaser
Mohamed")
19 instance_adapter.InsertCommand.Parameters.AddWithValue("?", "32")
20 instance_adapter.InsertCommand.Parameters.AddWithValue("?",
"0123456789")
21 instance_adapter.InsertCommand.Parameters.AddWithValue("?",
"Yaser1395@hotmail.com")

22 Me.BindingContext(ds, "Information").EndCurrentEdit()
33 instance_adapter.Update(ds, "Information")

24 MsgBox("Update Successfully...", MsgBoxStyle.Information)

‫شرح الكود السابق مطابق للشرح الذي يسبقه ما عدا بعض االختالفات‬
: ‫ وهي متزامنة مع أرقام األسطر‬، ‫التي سوف نوضحها هنا‬
‫ إلى مصدر‬DataSet ‫ ولكي نقوم بتصدير البيانات الجديده من الكائن‬-16
‫ التابع للكائن‬InsertCommand ‫البيانات نحتاج إلى اعداد وتجهيز كائن األمر‬
‫ والخاص بإضافة صف جديد وذلك من خالل إسناد كائن أمر‬DataAdapter
‫ و كائن االتصال الالزم‬، ‫ الالزمة لعملية اإلضافة‬SQL ‫جديد وإعداده بجملة‬
. ً‫أيضا‬
‫ بعد ذلك نحتاج إلى إضافة البارامترات التي تم تعشيشها في جملة‬-17
‫ وذلك نجده في كل من األسطر‬، ‫ مع قيمها المحدثة‬، ‫ السابقة الذكر‬SQL
. ) 21 ‫ إلى‬17 ( ‫رقم‬

: DeleteCommand ‫ األمر‬-
‫ و‬DataAdapte ‫ أحد خصائص الكائن‬DeleteCommand ‫يعد أمر الحذف‬
‫ والطريقة التي‬، ‫عمله األساسي هو حذف سجل من سجالت الجدول‬
‫يعمل بها مشابهة تماماً لكائن األمر الذي قد تحدثنا عنه في الوضع‬
‫ والتي‬SQL ‫ في جملة‬Where ‫ حيث سنحتاج إلى وجود فقرة‬، ‫المتصل‬
، ‫تشير إلى بارامتر ( معامل ) يساعدنا في تحديد السجل المطلوب حذفه‬
‫ حيث أنه في البداية سوف نقوم بتجهيز‬، ‫والمثال التالي موضح بالشرح‬
‫ و نقوم بحذف السجل‬، ‫ بالبيانات‬DataSet ‫ ثم نمأل الـ‬، ‫الكائنات للعمل‬
‫ ( تعتبر عمليه الحذف هذه‬DataSet ‫الحالي من الجدول المخزن في الكائن‬
، ) DataSet ‫تحديث للسجالت الموجودة في الجدول المخزن في الكائن‬
‫ بشكل عكسي من‬DataSet ‫ثم نقوم بنسخ التغييرات التي طرأت على الـ‬
‫ وذلك من خال الكائن‬، ‫ إلى مصدر قاعدة البيانات‬DataSet ‫الكائن‬
‫ وفي األخير‬، ‫ التابع له‬DeleteCommand ‫ وكائن األمر‬DataAdapter
‫ فلنلقي نظرة‬، ‫ لتولي مسؤلية عملية النسخ‬UpDate ‫نستخدم الخاصية‬
.. ‫متفحصة على الكود‬

‫كود‬
1 Dim selectCommandText As String = "SELECT * FROM Information order by
name"
2 Dim selectConnection As OleDbConnection = New OleDbConnection _
3     ("Provider=Microsoft.Jet.OLEDB.4.0;" _
4     & "Data Source=|DataDirectory|\Note.mdb;" _
5     & "User Id=admin;" _
6     & "Jet OLEDB:Database Password=ado.net;")

7 Dim instance_adapter As New OleDbDataAdapter(selectCommandText,


selectConnection)
8 Dim ds As New DataSet
9 instance_adapter.Fill(ds, "Information")

10 Dim MYPosition As String = Me.BindingContext(ds, "Information").Position


11 Dim IdValue As String =
ds.Tables("Information").Rows(MYPosition).Item("ID")

12 Me.BindingContext(ds, "Information").RemoveAt(MyPosition)

13 instance_adapter.DeleteCommand = New OleDbCommand("DELETE FROM


Information WHERE  Information.ID=?;", selectConnection)

14 instance_adapter.DeleteCommand.Parameters.Clear()
15 instance_adapter.DeleteCommand.Parameters.AddWithValue("?",IdValue)

16 Me.BindingContext(ds, "Information").EndCurrentEdit()
17 instance_adapter.Update(ds, "Information")

18 MsgBox("DELETED Successfully...", MsgBoxStyle.Information)

‫ لذا‬، ‫شرح الكود السابق ينطبق عليه بعض الشروحات التي سبق ذكرها‬
: ‫ وهو متزامن مع أرقام األسطر‬، ‫سنكتفي بشرج ما جد من الكود‬
‫ نقوم بتخزين الرقم الذي يعبّر عن موضع السجل الحالي في الجدول‬-10
‫المخزن داخل الكائن ‪ ، DataSet‬وذلك من أجل عملية الحذف من داخل الـ‬
‫‪. DataSet‬‬
‫‪ -11‬نقوم بتخزين قيمة رقم الحقل المسمى "‪ "ID‬الخاص بالسجل الحالي‬
‫للجدول المخزن داخل الكائن ‪ ، DataSet‬وذلك من أجل عملية الحذف من‬
‫مصدر قاعدة البيانات ‪.‬‬
‫‪ -12‬نقوم هنا باستخدام األمر ‪ RemoveAt‬لحذف السجل الحالي ( والذي‬
‫قمنا بتخزين قيمته في المتغير ‪ ) MYPosition‬من الجدول المخزن في‬
‫الكائن ‪ DataSet‬والذي قمنا بتحديد اسمه ‪ ،‬عندئذ يتم حذف السجل من‬
‫داخل الـ ‪ DataSet‬فقط ‪.‬‬
‫‪ -13‬ولكي نقوم بتصدير البيانات المحدثة من الكائن ‪ DataSet‬إلى مصدر‬
‫البيانات نحتاج إلى إعداد وتجهيز كائن األمر ‪ DeleteCommand‬التابع للكائن‬
‫‪ DataAdapter‬والخاص بحذف سجل ‪ ،‬وذلك من خالل إسناد كائن أمر‬
‫جديد وإعداده بجملة ‪ SQL‬الالزمة لعملية الحذف ‪ ،‬و كائن االتصال الالزم‬
‫أيضاً ‪.‬‬
‫‪ -14‬بعد ذلك نحتاج إلى إضافة البارامتر الذي تم تعشيشه في جملة ‪SQL‬‬
‫السابقة الذكر ‪ ،‬مع قيمته والتي قمنا بتخزينها سابقاً في المتغير‬
‫‪ ، MyFieldValue‬وذلك نجده في كل من السطرين رقم ( ‪. ) 15 ، 14‬‬

‫‪ -‬األمر ‪:UpdateCommand‬‬
‫يعد أمر التحديث ‪ UpdateCommand‬أحد خصائص الكائن ‪ DataAdapte‬و‬
‫عمله األساسي هو تحديث سجل محدد من سجالت الجدول ‪ ،‬والطريقة‬
‫التي يعمل بها مشابهة تماماً لكائن األمر الذي قد تحدثنا عنه في الوضع‬
‫المتصل ‪ ،‬حيث سنحتاج إلى وجود فقرة ‪ Where‬في جملة ‪ SQL‬والتي‬
‫تشير إلى بارامتر ( معامل ) يساعدنا في تحديد السجل المطلوب‬
‫تحديثه ‪ ،‬والمثال التالي موضح بالشرح ‪ ،‬حيث أنه في البداية سوف نقوم‬
‫بتجهيز الكائنات للعمل ‪ ،‬ثم نمأل الـ ‪ DataSet‬بالبيانات ‪ ،‬و نقوم بعد ذلك‬
‫بتحديث بيانات السجل الحالي للجدول المخزن في الكائن ‪ ، DataSet‬ثم‬
‫نقوم بنسخ التغييرات التي طرأت على الـ ‪ DataSet‬بشكل عكسي من‬
‫الكائن ‪ DataSet‬إلى مصدر قاعدة البيانات ‪ ،‬وذلك من خال الكائن‬
‫‪ DataAdapter‬وكائن األمر ‪ UpdateCommand‬التابع له ‪ ،‬وفي األخير‬
‫نستخدم الخاصية ‪ UpDate‬لتولي مسؤلية عملية النسخ ‪ ،‬فلنلقي نظرة‬
‫متفحصة على الكود ‪..‬‬

‫كود‬
‫"‪1 Dim selectCommandText As String = "SELECT * FROM Information‬‬
‫_ ‪2 Dim selectConnection As OleDbConnection = New OleDbConnection‬‬
‫_ ";‪3     ("Provider=Microsoft.Jet.OLEDB.4.0‬‬
‫_ ";‪4     & "Data Source=|DataDirectory|\Note.mdb‬‬
‫_ ";‪5     & "User Id=admin‬‬
‫)";‪6     & "Jet OLEDB:Database Password=ado.net‬‬

‫‪7 Dim instance_adapter As New OleDbDataAdapter(selectCommandText,‬‬


‫)‪selectConnection‬‬
8 Dim ds As New DataSet

9 instance_adapter.Fill(ds, "Information")

10 Dim MyPosition As Integer = Me.BindingContext(ds, "Information").Position


11 Dim IdValue As String = ds.Tables(0).Rows(MyPosition).Item("ID")

12 ds.Tables(0).Rows(MyPosition).Item("Name") = "Yaser AL-Shikh"


13 ds.Tables(0).Rows(MyPosition).Item("Age") = "32"
14 ds.Tables(0).Rows(MyPosition).Item("Phone") = "123456789"
15 ds.Tables(0).Rows(MyPosition).Item("Email") = "Yaser1395@hotmail.com"

16  instance_adapter.UpdateCommand = New OleDbCommand("UPDATE


Information set Name=?, Age=?, Phone=?, Email=? WHERE Id =?",
selectConnection)

17 instance_adapter.UpdateCommand.Parameters.Clear()
18 instance_adapter.UpdateCommand.Parameters.AddWithValue("?", "Yaser AL-
Shikh")
19 instance_adapter.UpdateCommand.Parameters.AddWithValue("?", "25")
20 instance_adapter.UpdateCommand.Parameters.AddWithValue("?",
"123456789")
21 instance_adapter.UpdateCommand.Parameters.AddWithValue("?",
"Yaser1395@hotmail.com")
22 instance_adapter.UpdateCommand.Parameters.AddWithValue("?", IdValue)

23 Me.BindingContext(ds, "Information").EndCurrentEdit()
24 instance_adapter.Update(ds.Tables("Information"))

25 MsgBox("Update Successfully...", MsgBoxStyle.Information)

‫ لذا‬، ‫شرح الكود السابق ينطبق عليه بعض الشروحات التي سبق ذكرها‬
: ‫ وهو متزامن مع أرقام األسطر‬، ‫سنكتفي بشرج ما جد من الكود‬
Name , Age ,Phone , ( ‫ نقوم هنا بتحديث حقول السجل الحالي‬-12
‫ وذلك نجده في كل‬، DataSet ‫ ) في الجدول المخزن داخل الكائن‬Email
) 15 ، 12 ( ‫من السطور رقم‬
‫ إلى مصدر‬DataSet ‫ ولكي نقوم بتصدير البيانات المحدثة من الكائن‬-16
‫التابع للكائن‬UpdateCommand ‫البيانات نحتاج إلى إعداد وتجهيز كائن األمر‬
‫ وذلك من خالل إسناد كائن أمر‬، ‫ والخاص بتحديث سجل‬DataAdapter
‫ و كائن االتصال الالزم‬، ‫ الالزمة لعملية التحديث‬SQL ‫جديد وإعداده بجملة‬
. ً‫أيضا‬
‫ بعد ذلك نحتاج إلى إضافة البارامترات التي تم تعشيشها في جملة‬-17
‫ وكذلك البارامتر الخاص بتحديد‬، ‫ مع قيمها الخاصة بها‬، ‫ السابقة الذكر‬SQL
‫السجل المراد تحديثه والذي قمنا بتخزين قيمته سابقاً في المتغير‬
. ) 22 ‫ إلى‬17 ( ‫ وكل ذلك نجده في السطور رقم‬، IdValue

Update ‫وقبل أن نختم الحديث عن عملية التحديث باستخدام الخاصية‬


‫أريد أن أبين أنه عند قيام هذا األمر بالعمل فإنه يقوم باختبار حالة الصف ‪،‬‬
‫ويقوم بتنفيذ األمر المطلوب سواء كان إضافة أو تحديث أو حذف ‪ ،‬وذلك‬
‫بشكل متكرر لكل صف من الصفوف ‪ ،‬حيث أن تحديث الصفوف ال يتم دفعة‬
‫واحده ‪ ،‬إنما يتم تحديث كل صف بشكل منفرد ‪.‬‬
‫وبمجرد استدعاء هذه الخاصية تقوم بتنفيذ سبعة أمور تتم خلف الكواليس‬
‫بشكل متالحق ‪ ،‬كنت قد أشرت إليها في إحدى ردودي على األخ‬
‫‪ ، abetry‬وهي كما يلي على التوالي ‪..‬‬

‫نتقل إلى قِيَم‬


‫ُ‬ ‫‪ .1‬القيم التي أدخلت في ‪ DataRow‬للكائن ‪ DataSet‬تَ‬
‫جمل ‪ SQL‬الخاصة بالتحديث ( اإلضافة أو‬ ‫البارامترات التي تم وضعها في ُ‬
‫التعديل أو الحذف ) سواء تم بناء هذه الجمل من قبل ‪CommandBuilder‬‬
‫أو التي وضعت من قبل المبرمج بشكل يدوي ‪.‬‬
‫‪ .2‬يبدأ الحدث ‪ OnRowUpdating‬في اإلقالع ‪.‬‬
‫‪ .3‬يتم تنفيذ أمر التحديث الذي تم كتابته في جملة ‪ ( SQL‬سواء كان هذا‬
‫التحديث لإلضافة أو التعديل أو الحذف ) ‪.‬‬
‫السجل األول ‪ ،‬فإن النتيجة األولى التي تعود يكون‬
‫ِ‬ ‫إلرجاع‬
‫‪ .4‬إذا األمر أعد ْ‬
‫موقعها في الـ ‪* . DataRow‬‬
‫ضوعون في الـ ‪* .DataRow‬‬ ‫‪ .5‬إذا كان هناك بارامترات ناتجه‪ ،‬فإنهم يو ُ‬
‫‪ .6‬يبدأ الحدث ‪ OnRowUpdated‬في اإلقالع ‪.‬‬
‫‪ .7‬يتم استدعاء الخاصية ‪ AcceptChanges‬من الكائن ‪. DataSet‬‬

‫* رقمي ( ‪ ) 5 ، 4‬لست على دراية تامة بها ولكني نقلتها بعد أن قمت‬
‫بترجمتها من ‪ MSDN‬لعله يوجد من يستفيد منها أو يوضحها لنا ‪ ،‬ونصها‬
‫األساسي هو ‪:‬‬
‫[‪]ltr‬‬
‫‪If the command is set to FirstReturnedRecord, the first returned -4‬‬
‫‪.result is placed in the DataRow‬‬
‫‪.If there are output parameters, they are placed in the DataRow -5‬‬
‫[‪]ltr/‬‬

‫مالحظة هامة ‪ :‬يمكن التوسع في هذا األمر ‪ ،‬ولكنا سنكتفي بهذا الحد‬
‫من المعلومات ‪ ،‬ومن الجدير بالذكر أننا لم نستخدم بعد عناصر التحكم‬
‫المترابطة ‪ ،‬وكذلك باني األوامر الكائن ‪ CommandBuilder‬والكائنات األخرى‬
‫‪ ،‬والتي تختصر لنا الكود بنسبة مرضية جداً ‪ ،‬باإلضافة إلى المرونة وقوة‬
‫التحكم ‪ ،‬من أجل ذلك نجد هنا أن الكود طويل ( أرجو أن نصبر قليال ً فهو‬
‫في حقيقة األمر اقل بكثير مما هو عليه) ‪ ،‬علماً أن الكود المذكور من أجل‬
‫الشرح والفهم لما يدور خلف الكواليس فقط ‪ ،‬وسوف نعيد كتابة االكواد‬
‫السابقة بشكل مختصر ‪ ،‬وذلك عندما نتقدم في الدرس قليال ً ‪..‬‬

‫** هنا ينتهي حديثنا اليوم ‪ ،‬ونأخذ قسطاً من الراحة ‪ ،‬لكي نستوعب هذه‬
‫الجرعة من المعلومات ونصحح أخطائها إن وجدت ‪ ،‬ونعلق على الغير‬
‫مفهوم منها ‪ ،‬و نكمل فيما بعد شرح ما تبقى من الدرس وذلك في‬
‫المشاركات القادمة ‪..‬‬

‫مالحظة ‪ :‬موضوعنا القادم سوف يكون الكائن ‪ ، CommandBuilder‬وسوف‬


‫نتعلم كيف يختصر لنا هذا الكائن الكثير من األكواد السابقه ‪ ،‬كما‬
‫سنشاهد كيف يقوم هذا الكائن ببناء األوامر الخصة بالتحديث ‪ ..‬إلى أن‬
‫نلتقي مرة أخرى أترككم في حفظ هللا ورعايته ‪ ..‬ياسر الشيخ‬

‫((سبحانك اللهم وبحمدك‪ ،‬أشهد أن ال إله إال أنت‪ ،‬أستغفرك وأتوب إليك‬

‫=============‬

‫الكائن ‪: CommandBuilder‬‬
‫من المعلوم أن الكائن ‪ DataAdapter‬يستخدم كائنات األوامر (‬
‫‪SelectCommand ، InsertCommand ، UpdateCommand ،‬‬
‫‪ ) DeleteCommand‬الختيار أو إضافة أو تحديث أو حذف السجالت في‬
‫قاعدة البيانات ‪ ،‬وحيث أنه يجب على هذه الكائنات استخدام الكائن‬
‫‪ ( OleDbParamter‬أو ‪ ) SqlParamter‬لتحديد القيم التي يجب استخدامها‬
‫لمعالجة قاعدة البيانات ‪ ،‬فإنه ولحسن الحظ ال يتوجب علينا إنشاء تلك‬
‫الكائنات ( اإلضافة أو التحديث أو الحذف وكذلك البارامترات ) بأنفسنا ‪،‬‬
‫وذلك عندما نستخدم الكائن ‪ ( CommandBuilder‬والذي يسمى باني‬
‫األوامر ) و القيام بربطه بالكائن ‪ ، DataAdapter‬حيث سوف يتولى هو‬
‫بإعداد األوامر الالزمة و البارامترات المناسبة ‪.‬‬
‫وطرقة تجهيزه للعمل بسيطة جداً ‪ ،‬وهي كما في الكود التالي ‪..‬‬

‫كود‬
‫‪Dim MyComB As New OleDbCommandBuilder‬‬
‫‪MyComB.DataAdapter = instance_adapter‬‬

‫ويمكن اختصارها بالشكل التالي ‪..‬‬

‫كود‬
‫)‪Dim MyComB As New OleDbCommandBuilder(instance_adapter‬‬
‫ لصنع كائن‬CommandBuilder ‫ ويبين الكود التالي كيف يمكن استخدام‬-
.. ‫ إلضافة سجل جديد‬DataAdapter ‫أمر‬
، ‫وسوف نستخدم نفس الكود السابق شرحه مع بعض التعديالت الجديدة‬
‫ وفي السطر‬، DataAdapter ‫ بالكائن‬CommandBuilder ‫وهي ربط الكائن‬
CommandBuilder ‫ التي قام‬SQL ‫ نعرض فيها جملة‬MsgBox ‫األخير نعرض‬
.. ‫ببنائها وكذلك عدد البارامترات التي استخدمها‬

‫كود‬
Dim selectCommandText As String = "SELECT * FROM Information;"
Dim selectConnection As OleDbConnection = New OleDbConnection _
    ("Provider=Microsoft.Jet.OLEDB.4.0;" _
    & "Data Source=|DataDirectory|\Note.mdb;" _
    & "User Id=admin;" _
    & "Jet OLEDB:Database Password=ado.net;")
Dim instance_adapter As New OleDbDataAdapter(selectCommandText,
selectConnection)
Dim ds As New DataSet
instance_adapter.Fill(ds, "Information")

Dim MyDR As DataRow = ds.Tables("Information").NewRow


MyDR.Item("Name") = "Osamh Mohamed"
MyDR.Item("Age") = "18"
MyDR.Item("Phone") = "0123456789"
MyDR.Item("Email") = "Osamh@hotmail.com"
ds.Tables("Information").Rows.Add(MyDR)

Dim MyComB As New OleDbCommandBuilder(instance_adapter)

Me.BindingContext(ds, "Information").EndCurrentEdit()
instance_adapter.Update(ds, "Information")

MsgBox("Update Successfully...", MsgBoxStyle.Information)


MessageBox.Show(String.Format("The SQL InsertCommand Is :{0}{1}{2}{3}
{4}The Count Of Paramters Is: {5}", vbNewLine, vbNewLine,
MyComB.GetInsertCommand().CommandText, vbNewLine, vbNewLine,
MyComB.GetInsertCommand().Parameters.Count))

‫ كون‬CommandBuilder ‫بعد تنفيذ الكود السابق سوف نشاهد أن الكائن‬


‫ وأن عدد‬، ‫ الخاصة بإضافة سجل جديد بشكل احترافي‬SQL ‫لنا جملة‬
.. 4 ‫البارامترات التي استخدمها‬

SQL
INSERT INTO Information (Name, Age, Phone, Email) VALUES (?, ?, ?, ?)
‫ لصنع كائن‬CommandBuilder ‫ ويبين الكود التالي كيف يمكن استخدام‬-
.. ‫ لحذف سجل‬DataAdapter ‫أمر‬
، ‫وسوف نستخدم نفس الكود السابق شرحه مع بعض التعديالت الجديدة‬
‫ وفي السطر‬، DataAdapter ‫ بالكائن‬CommandBuilder ‫وهي ربط الكائن‬
CommandBuilder ‫ التي قام‬SQL ‫ نعرض فيها جملة‬MsgBox ‫األخير نعرض‬
.. ‫ببنائها وكذلك عدد البارامترات التي استخدمها‬

‫كود‬
Dim selectCommandText As String = "SELECT * FROM Information order by
name"
Dim selectConnection As OleDbConnection = New OleDbConnection _
    ("Provider=Microsoft.Jet.OLEDB.4.0;" _
    & "Data Source=|DataDirectory|\Note.mdb;" _
    & "User Id=admin;" _
    & "Jet OLEDB:Database Password=ado.net;")
Dim instance_adapter As New OleDbDataAdapter(selectCommandText,
selectConnection)
Dim ds As New DataSet
instance_adapter.Fill(ds, "Information")

Dim MYPosition As String = Me.BindingContext(ds, "Information").Position

Me.BindingContext(ds, "Information").RemoveAt(MYPosition)

Dim MyComB As New OleDbCommandBuilder(instance_adapter)

Me.BindingContext(ds, "Information").EndCurrentEdit()
instance_adapter.Update(ds, "Information")

MsgBox("DELETED Successfully...", MsgBoxStyle.Information)


MessageBox.Show(String.Format("The SQL DeleteCommand Is :{0}{1}{2}{3}
{4}The Count Of Paramters Is: {5}", vbNewLine, vbNewLine,
MyComB.GetDeleteCommand().CommandText, vbNewLine, vbNewLine,
MyComB.GetDeleteCommand().Parameters.Count))

‫ كون‬CommandBuilder ‫بعد تنفيذ الكود السابق سوف نشاهد أن الكائن‬


‫ وأن عدد البارامترات التي‬، ‫ الخاصة بالحذف بشكل احترافي‬SQL ‫لنا جملة‬
.. 9 ‫استخدمها‬

SQL
DELETE FROM Information WHERE ((ID = ?) AND ((? = 1 AND Name IS NULL)
OR (Name = ?)) AND ((? = 1 AND Age IS NULL) OR (Age = ?)) AND ((? = 1 AND
Phone IS NULL) OR (Phone = ?)) AND ((? = 1 AND Email IS NULL) OR (Email
= ?)))
‫ لصنع كائن‬CommandBuilder ‫ ويبين الكود التالي كيف يمكن استخدام‬-
.. ‫ لتحديث بيانات سجل‬DataAdapter ‫أمر‬
، ‫وسوف نستخدم نفس الكود السابق شرحه مع بعض التعديالت الجديدة‬
‫ وفي السطر‬، DataAdapter ‫ بالكائن‬CommandBuilder ‫وهي ربط الكائن‬
CommandBuilder ‫ التي قام‬SQL ‫ نعرض فيها جملة‬MsgBox ‫األخير نعرض‬
.. ‫ببنائها وكذلك عدد البارامترات التي استخدمها‬

‫كود‬
Dim selectCommandText As String = "SELECT * FROM Information order by
name"
Dim selectConnection As OleDbConnection = New OleDbConnection _
    ("Provider=Microsoft.Jet.OLEDB.4.0;" _
    & "Data Source=|DataDirectory|\Note.mdb;" _
    & "User Id=admin;" _
    & "Jet OLEDB:Database Password=ado.net;")
Dim instance_adapter As New OleDbDataAdapter(selectCommandText,
selectConnection)
Dim ds As New DataSet

instance_adapter.Fill(ds, "Information")

ds.Tables(0).Rows(3).Item("Name") = "Yaser AL-Shikh"


ds.Tables(0).Rows(3).Item("Age") = "32"
ds.Tables(0).Rows(3).Item("Phone") = "0123456789"
ds.Tables(0).Rows(3).Item("Email") = "yaser1395@hotmail.com"

Dim MyComB As New OleDbCommandBuilder(instance_adapter)

Me.BindingContext(ds, "Information").EndCurrentEdit()
instance_adapter.Update(ds, "Information")

MsgBox("Update Successfully...", MsgBoxStyle.Information)


MessageBox.Show(String.Format("The SQL UpdateCommand Is :{0}{1}{2}{3}
{4}The Count Of Paramters Is: {5}", vbNewLine, vbNewLine,
MyComB.GetUpdateCommand().CommandText, vbNewLine, vbNewLine,
MyComB.GetUpdateCommand().Parameters.Count))

‫ كون‬CommandBuilder ‫بعد تنفيذ الكود السابق سوف نشاهد أن الكائن‬


‫ وأن عدد‬، ‫ الخاصة بتحديث بيانات سجل بشكل احترافي‬SQL ‫لنا جملة‬
.. 13 ‫البارامترات التي استخدمها‬

SQL
UPDATE Information SET Name = ?, Age = ?, Phone = ?, Email = ? WHERE ((ID
= ?) AND ((? = 1 AND Name IS NULL) OR (Name = ?)) AND ((? = 1 AND Age IS
‫?(( ‪NULL) OR (Age = ?)) AND ((? = 1 AND Phone IS NULL) OR (Phone = ?)) AND‬‬
‫)))? = ‪= 1 AND Email IS NULL) OR (Email‬‬

‫وقبل أن نختم الحديث عن ‪ CommandBuilder‬أريد أن أوضح شيء مهم‬


‫جداً ‪ ،‬وهو أنه قد يحدث عند تعبئتك للكائن ‪ DataSet‬بالجدول من خالل‬
‫استخدام جملة ) ‪ ، SQL ( Select‬تكون هذه الجملة قد احتوت على‬
‫استخدام األقواس ( ] [ ) أو على فراغات في تسمية الجدول أو الحقول ‪،‬‬
‫أو أن ألجملة تحتوي على كلمات محجوزة للغة ‪ SQL‬في تسمية الحقول‬
‫مثل ‪ MEMO‬و ‪ ، DATE‬في هذه الحالة يجب علينا إضافة القوسين ( [ ] )‬
‫لجمل ‪SQL ( InsertCommand ، UpdateCommand ،‬‬
‫) ‪DeleteCommand‬التي ينشئها باني األوامر ‪ ،‬ويتم ذلك من خالل‬
‫الخاصيتان ‪ QuotePrefix‬و ‪ ، QuoteSuffix‬وطرقة عملها سهل جداً ‪ ،‬والكود‬
‫التالي يوضح الطريقة الصحيحة للتحديث من خالل باني األوامر في مثل‬
‫هذه الحاالت ‪..‬‬

‫كود‬
‫)‪Dim MyComB As New OleDbCommandBuilder(instance_adapter‬‬

‫"[" = ‪MyComB.QuotePrefix‬‬
‫"]" = ‪MyComB.QuoteSuffix‬‬

‫‪instance_adapter.InsertCommand = MyComB.GetInsertCommand‬‬
‫‪instance_adapter.UpdateCommand = MyComB.GetUpdateCommand‬‬
‫‪instance_adapter.DeleteCommand = MyComB.GetDeleteCommand‬‬

‫)(‪Me.BindingContext(ds, "Information").EndCurrentEdit‬‬
‫)"‪instance_adapter.Update(ds, "Information‬‬

‫** هنا ينتهي حديثنا اليوم ‪ ،‬ونأخذ قسطاً من الراحة ‪ ،‬لكي نستوعب هذه‬
‫الجرعة من المعلومات ونصحح أخطائها إن وجدت ‪ ،‬ونعلق على الغير‬
‫مفهوم منها ‪ ،‬و نكمل فيما بعد شرح ما تبقى من الدرس وذلك في‬
‫المشاركات القادمة ‪..‬‬

‫مالحظة ‪ :‬موضوعنا القادم سوف يكون " ربط عناصر التحكم ‪Binding‬‬
‫‪ ، " Controls‬وسوف نتعلم كيف يمكننا إنعاش هذه العناصر وجعلها تنبض‬
‫بالحياة ‪ ..‬إلى أن نلتقي مرة أخرى أترككم في حفظ هللا ورعايته ‪ ..‬ياسر‬
‫الشيخ‬

‫((سبحانك اللهم وبحمدك‪ ،‬أشهد أن ال إله إال أنت‪ ،‬أستغفرك وأتوب إليك ))‬
‫ربط عناصر التحكم ‪-: Binding Controls‬‬

‫مقدمة ‪ :‬تقوم فكرة ربط عناصر التحكم على ربط البيانات المتوفرة في‬
‫‪ DataTable‬أو ‪ DataSet‬بأحد عناصر التحكم الموجودة في واجهة‬
‫المستخدم الرسومية على سبيل المثال ‪:‬‬
‫‪CheckBox ، ComboBox ، DateTimePicker ، Label ، LinkLabel ،‬‬
‫‪، ListBox‬‬
‫‪ListView ، MonthCalendar ، PictureBox ، ProgressBar ، RadioButton‬‬
‫‪،‬‬
‫‪... RichTextBox ، StatusBar ، TextBox ، TreeView ، DataGridView‬‬
‫هذا فقط على سبيل المثال ال الحصر ‪ ،‬وبعد أن يتم ربط عناصر التحكم‬
‫بمصدر البيانات ‪ ،‬فإنه يتم إنعاشها فتصبح تنبض بالحياة ‪ ،‬حيث أن أي‬
‫تغييرات تتم على مصدر البيانات ستنعكس تلقائياً عليها وكذلك العكس‬
‫حيث أن أي تغيير يتم على قيم هذه العناصر فإنه ينعكس على مصدر‬
‫البيانات وفي هذه الحالة لو استدعينا الخاصية ‪ HasChanges‬التابعة للكائن‬
‫‪ DataSet‬فإنها سوف تعيد القيمة ‪ . True‬إضافة إلى أن الربط فيما بين‬
‫عناصر التحكم ومصادر البيانات يسهل علينا عملية البحث و اإلبحار والتنقل‬
‫بين البيانات الن عناصر التحكم في هذا الحالة سوف تعرض القيم الحالية‬
‫للبيانات المتوفرة في مصادر البيانات ‪.‬‬

‫طرق ربط البيانات بعناصر التحكم ‪-:‬‬


‫هناك طريقتان لربط البيانات بعناصر التحكم ‪ ،‬أوالهما ‪Binding at design‬‬
‫‪ time‬أي الربط في وقت التصميم ويتم باستخدام األدوات والمعالجات ‪،‬‬
‫واألخرى ‪ Binding at run time‬أي الربط في وقت التشغيل ويتم باستخدام‬
‫الكود وهو ما سنقوم بدراسته هنا ‪.‬‬

‫أنواع ربط البيانات بعناصر التحكم ‪-:‬‬


‫هناك نوعان من الربط ويعتمدان على نوع عنصر التحكم الذي نريد أن نربطه‬
‫بمصدر البيانات ‪..‬‬

‫* النوع األول ويسمى الربط البسيط ( ‪-: ) Simple-bind control‬‬

‫وهو يختص بعناصر التحكم التي تستقبل ( ‪ ) single value‬أي قيمة حقل‬
‫واحد من مصدر البيانات على سبيل المثال ‪TextBox ، Lable ،‬‬
‫‪DateTimePicker ، PictureBox ، MonthCalendar‬‬
‫وللقيام بعملية الربط سوف نستخدم الكائن ‪ ، Binding‬ونقوم بتجهيزه‬
‫للعمل وذلك عن طريق تزويده ببعض المتغيرات ( البارامترات ) وهي‬
‫كالتالي ‪:‬‬

‫‪: PropertyName -1‬‬


‫( ‪) The name of the control property to bind‬‬
‫وهو يشير إلى الخاصية الخاصة بأداة التحكم والتي سوف تستورد القيمة‬
‫النصية القادمة من قاعدة البيانات وتعرضها في النموذج ‪ ،‬ومن خاللها أيضا‬
‫نقوم بتصدير القيمة المخزنة فيها إلى قاعدة البيانات وذلك عند عملية‬
‫التحديث ‪ .‬وهي تختلف من أداة تحكم إلى أداة أخرى ‪ ،‬فهي يمكن أن‬
‫تأخذ إحدى القيم التالية ((‪Text ، Tag ، Value ، Checked ، Image ،‬‬
‫‪. )) SelectedValue‬‬

‫‪: DataSource -2‬‬


‫( ‪) An Object that represents the data source‬‬
‫وهو يشير إلى مصدر البيانات ‪ ،‬ونقصد بمصدر البيانات هنا الكائن الذي‬
‫يحتوي على جدول أو جداول قاعدة البيانات ‪ ،‬ونستطيع أن نحدد على‬
‫سبيل المثال أي كائن من الكائنات التالية كمصدر بيانات (( ‪DataSet ،‬‬
‫‪)) DataTable ، DataView ، DataViewManager ، BindingSource‬‬

‫‪: DataMember -3‬‬


‫(‪) The property or list to bind to‬‬
‫وهو يشير إلى قيمة نصية تحمل اسم العمود الموجود في جدول قاعدة‬
‫البيانات والذي نريد أن نعرض بيانات حقوله في أداة التحكم ‪.‬‬

‫‪: formattingEnabled -4‬‬


‫( ‪) true to format the displayed data; otherwise, false‬‬
‫وهو متغير اختياري ‪ ،‬له قيمتان إما صائب ‪ True‬يسمح بقراءة القيمة‬
‫المخزنة وتهيئها وتحويلها من صيغة إلى صيغة أخرى أو خاطئ ‪ False‬وهو‬
‫عكس السابق ‪ ،‬وهو مهم جدا لبعض األدوات أعلم منها ‪ PictureBox‬حيث‬
‫يجب أن نجعله ‪ True‬من أجل تحويل القيمة المخزنة في قاعدة البيانات‬
‫من ‪ byte‬إلى ‪ Bitmap‬كي تتمكن هذه األداة من عرض الصور ( اجتهاد‬
‫فردي مني ‪ ،‬وهللا أعلم ) ‪.‬‬

‫وبعد تجهيز الكائن ‪ Binding‬نقوم بإضافته للخاصية ‪ DataBindings‬التابعة‬


‫ألداة التحكم ‪ .‬وبذلك نكون قد انتهينا من عملية الربط وأصبحت أداة التحكم‬
‫متصلة بمصدر البيانات بشكل مباشر ‪ .‬ولعل المثال التالي يساعد في‬
‫توضيح ما سبق ‪:‬‬
‫لنفرض أن لدينا كائن ‪ DataSet‬كمصدر بيانات وفيه جدول يحمل اسم‬
‫‪ Information‬ويوجد بهذا الجدول عمود يحمل اسم ‪ ، Name‬ونريد أن نربط‬
‫الخاصية ‪ Text‬التابعة ألداة التحكم ‪ TextBox‬بقيم حقول العمود ‪، Name‬‬
‫كي يتم ذلك فلنلقي نظرة على الكود التالي ‪..‬‬

‫كود‬
Dim bindName As Binding
bindName = New Binding("Text", MyDataSet.Tables("information"), "Name")
TextBox.DataBindings.Add(bindName)

.. ‫يمكننا كتابة الكود السابق بالشكل التالي‬

‫كود‬
Dim bindName As Binding
bindName = New Binding("Text", MyDataSet, "information.Name")
TextBox.DataBindings.Add(bindName)

.. ‫ويكن اختصار الكود السابق بالشكل التالي‬

‫كود‬
Dim bindName As New Binding("Text", MyDataSet, "information.Name")
TextBox.DataBindings.Add(bindName)

.. ‫ويكن اختصار الكود السابق بالشكل التالي‬

‫كود‬
TextBox.DataBindings.Add(New Binding("Text", MyDataSet, "information.Name"))

‫بعد ذلك سوف نجد أن أداة التحكم تعرض لنا قيمه أول حقل من حقول‬
‫ واإلبحار‬Name ‫ وعند القيام بالتنقل بين حقول العمود‬، Name ‫العمود‬
TextBox ‫ سنجد أن القيمة النصية المخزنة في أداة التحكم‬، ‫وكذلك البحث‬
. ‫تتغير بتغير مواضع تلك الحقول‬
: ‫وهذه بعض الصور لعمليات الربط البسيط لبعض أدوات التحكم‬

‫كود‬
TextBox.DataBindings.Add(New Binding("Text", MyDataSet, "information.Name"))
TextBox.DataBindings.Add(New Binding("Tag", MyDataSet, "information.Name"))
Lable.DataBindings.Add(New Binding("Text", MyDataSet,
"information.ID_Student"))
CheckBox.DataBindings.Add(New Binding("Checked", MyDataSet,
"information.Good"))
CheckBox.DataBindings.Add(New Binding("Tag",
MyDataSet.Tables("information"), "Good")
DateTimePicker1.DataBindings.Add(New Binding("Text", MyDataSet,
"information.date"))
DateTimePicker1.DataBindings.Add(New Binding("Value", MyDataSet,
"information.date"))
PictureBox.DataBindings.Add(New Binding("Image", MyDataSet, "imageData",
True))
‫* النوع الثاني ويسمى الربط المركب ( ‪: ) Complex-bind control‬‬
‫وهو يختص بعناصر التحكم التي تستقبل ( ‪ ) multiple values‬أي عدة قيم‬
‫( ‪ ) DataColumn‬من مصدر البيانات مثل ‪ ComboBox ، ListBox‬أو مصدر‬
‫بيانات كامل ( ‪ ) DataTable‬مثال ‪ . DataGridView‬وللقيام بعملية الربط‬
‫هناك أكثر من أسلوب وبعضها مختصر ورائع ‪ ،‬وسوف نتدرج في الشرح إلى‬
‫أن نصل لألسلوب األفضل واألسهل ‪ .‬ومن الجدير بالذكر أننا في الربط‬
‫المركب نستطيع أن نربط أدوات التحكم إما لعرض البيانات فقط أو للعرض‬
‫والتحديث ‪ ،‬وسنتناول الطريقتين في السطور القادمة ‪.‬‬
‫يجب في البداية تجهيز بعض خصائص أداة التحكم ‪ ،‬وهي تختلف من أداة‬
‫تحكم إلى أداة تحكم أخرى ‪ ،‬وهي كما يلي ‪:‬‬

‫‪ -1‬الخاصية ‪: DataSource‬‬
‫ونقوم بتمرير مصدر البيانات لهذه الخاصية ‪ ،‬ونقصد بمصدر البيانات هنا‬
‫الكائن الذي يحتوي على جدول قاعدة البيانات والذي نريد أن نعرض بياناته‬
‫على هذه األداة ‪ ،‬ونستطيع أن نحدد على سبيل المثال أي كائن من‬
‫الكائنات التالية كمصدر بيانات لهذه الخاصية (( ‪DataSet ، DataTable ،‬‬
‫‪)) DataView ، DataViewManager ، BindingSource‬‬

‫‪: DisplayMember -2‬‬


‫ونقوم بتمرير قيمة نصية لهذه الخاصية تحمل اسم العمود الموجود في‬
‫جدول قاعدة البيانات والذي نريد أن نعرض بيانات حقوله في أداة التحكم ‪.‬‬

‫‪ ( ValueMember -3‬اختياري ) ‪:‬‬


‫وتظهر أهمية هذه الخاصية في حالة كنا نريد أن نعرض للمستخدم قيمة‬
‫أحد األعمدة ( وذلك باستخدام الخاصية " ‪ " DisplayMember‬التي تحدثنا‬
‫عنها منذ قليل ) ونخزن في قاعدة البيانات قيمة عمود أخر ‪ ،‬حيث أننا‬
‫سوف نقوم بتمرير قيمة نصية لهذه الخاصية تحمل اسم العمود األخر ‪.‬‬

‫‪ ( DataBindings -4‬اختياري )‪:‬‬


‫وتظهر أهمية هذه الخاصية في حالة كنا نريد أن نحفظ في قاعدة البيانات‬
‫القيمة التي تعرضها أداة التحكم أو القيمة المخزنة في الخاصية‬
‫‪ ValueMember‬والتي تحدثنا عنها منذ قليل ‪ ،‬ولتجهيز هذه الخاصية سوف‬
‫نتبع نفس األسلوب الذي اتبعناه في الربط البسيط والذي تحدثنا عنه‬
‫بالتفصيل فيما سبق ‪.‬‬

‫أمثلة تطبيقية على الربط المركب ألدوات التحكم ( ‪Complex-bind control‬‬


‫)‪:‬‬

‫‪ -1‬بفرض أننا نريد أن نربط أداة تحكم من نوع ‪ ListBox‬بقيم العمود الذي‬
‫يحمل اسم " ‪ " Name‬وهو أسماء الطالب ‪ ،‬علما أن هذا العمود أحد‬
‫أعمدة الجدول ‪ Students‬وهذا الجدول مخزن في الكائن ‪ ، DataSet‬وللقيام‬
‫بعملية الربط فلنتفحص الكود التالي ‪..‬‬

‫كود‬
‫‪ListBox1.DataSource = MyDataSet‬‬
‫"‪ListBox1.DisplayMember = "Students.name‬‬

‫ويمكن كتابة الكود السابق بشكل آخر ‪..‬‬

‫كود‬
‫)"‪ListBox1.DataSource = MyDataSet.Tables("Students‬‬
‫"‪ListBox1.DisplayMember = "name‬‬

‫‪ -2‬بفرض أننا نريد أن نمأل أداة التحكم من نوع ‪ ComboBox‬بالقيم ( ‪Male ,‬‬
‫‪ ) Female‬وذلك بالطريقة التقليدية ‪ ،‬ولكنا أيضا نريد أن نجهز األداة نفسها‬
‫كي تكون قادرة على حفظ القيمة التي يختارها المستخدم من القائمة‬
‫المنسدلة لها في العمود الذي يحمل اسم " ‪ " CustomerSex‬علما أن‬
‫هذا العمود أحد أعمدة الجدول ‪ Customers‬وهذا الجدول مخزن في الكائن‬
‫‪ ، DataSet‬وللقيام بعملية الربط و التجهيز للحفظ فلنتفحص الكود التالي ‪..‬‬

‫كود‬
‫)"‪ComboBox1.Items.Add("Male‬‬
‫)"‪ComboBox1.Items.Add("Female‬‬
‫‪ComboBox1.DataBindings.Add(New Binding("Text",‬‬
‫))"‪MyDataSet.Tables("Customers"), "CustomerSex‬‬

‫‪ -3‬بفرض أننا نريد أن نربط أداة تحكم من نوع ‪ ComboBox‬بقيم العمود‬


‫الذي يحمل اسم " ‪ " City‬وهو أسماء المدن ‪ ،‬علما أن هذا العمود أحد‬
‫أعمدة الجدول ‪ Information‬وهذا الجدول مخزن في الكائن ‪، DataSet‬‬
‫ونريد أيضا أن نجهز نفس األداة كي تكون قادرة على حفظ القيمة التي‬
‫يختارها المستخدم من القائمة المنسدلة لها في العمود الذي يحمل اسم‬
‫" ‪ " CustomerCity‬علما أن هذا العمود أحد أعمدة الجدول‬
‫‪ CustomerAddresses‬وهذا الجدول مخزن في الكائن ‪ DataSet‬أيضاً ‪،‬‬
‫وللقيام بعملية الربط و التجهيز للحفظ فلنتفحص الكود التالي ‪..‬‬

‫كود‬
‫)"‪ComboBox1.DataSource = MyDataSet.Tables("Information‬‬
‫"‪ComboBox1.DisplayMember = "City‬‬
‫‪ComboBox1.DataBindings.Add(New Binding("text",‬‬
‫))"‪MyDataSet.Tables("CustomerAddresses"), " CustomerCity‬‬
‫‪ -4‬بفرض أننا نريد أن نربط أداة تحكم من نوع ‪ ComboBox‬بقيم العمود‬
‫الذي يحمل اسم " ‪ " City‬وهو أسماء المدن وذلك من أجل عرض أسماء‬
‫المدن للمستخدم ‪ ،‬و نريد من األداة نفسها أن تحتفظ بقيم العمود الذي‬
‫يحمل اسم " ‪ " ID_City‬وهو يدل على أرقام المدن وذلك من أجل عملية‬
‫الحفظ فيما بعد ‪ ،‬علما أن هذين العمودين من أعمدة الجدول ‪Information‬‬
‫وهذا الجدول مخزن في الكائن ‪ ، DataSet‬ونريد أيضا أن نجهز نفس األداة‬
‫كي تكون قادرة على حفظ القيمة التي تحتفظ بها وليس التي تعرض‬
‫للمستخدم والتي يختارها المستخدم من القائمة المنسدلة لها ‪ ،‬في‬
‫العمود الذي يحمل اسم " ‪ " CustomerCity‬علما أن هذا العمود أحد‬
‫أعمدة الجدول ‪ CustomerAddresses‬وهذا الجدول مخزن في الكائن‬
‫‪ DataSet‬أيضاً ‪ ،‬وللقيام بعملية الربط و التجهيز للحفظ فلنتفحص الكود‬
‫التالي ‪..‬‬

‫كود‬
‫)"‪ComboBox1.DataSource = MyDataSet.Tables("Information‬‬
‫"‪ComboBox1.DisplayMember = "City‬‬
‫"‪ComboBox1.ValueMember = "ID_City‬‬
‫‪ComboBox1.DataBindings.Add(New Binding("selectedValue",‬‬
‫))"‪MyDataSet.Tables("CustomerAddresses"), " CustomerCity‬‬

‫‪ -5‬بفرض أننا نريد أن نربط مصدر بيانات كامل مثل الجدول‬


‫‪ ( CustomerAddresses‬هذا الجدول مخزن في الكائن ‪ ) DataSet‬في أداة‬
‫التحكم ‪ DataGridView‬وذلك من أجل عرض البيانات وحتى التعديل عليها‬
‫وحفظها فيما بعد ‪ ،‬فإن األمر بغاية السهولة ‪ ،‬وللقيام بعملية الربط و‬
‫التجهيز للحفظ فلنتفحص الكود التالي ‪..‬‬

‫كود‬
‫‪DataGridView1.DataSource = MyDataSet‬‬
‫"‪DataGridView1.DataMember = "CustomerAddresses‬‬

‫ويمكن اختصار الكود كي يصبح بالشكل التالي ‪..‬‬

‫كود‬
‫)"‪DataGridView1.DataSource = MyDataSet.Tables("CustomerAddresses‬‬

‫** هنا ينتهي حديثنا اليوم ‪ ،‬ونأخذ قسطاً من الراحة ‪ ،‬لكي نستوعب هذه‬
‫الجرعة من المعلومات ونصحح أخطائها إن وجدت ‪ ،‬ونعلق على الغير‬
‫مفهوم منها ‪ ،‬و نكمل فيما بعد شرح ما تبقى من الدرس وذلك في‬
‫المشاركات القادمة ‪..‬‬

‫مالحظة ‪ :‬موضوعنا القادم سوف يكون " الكائن ‪ ، " BindingSource‬وسوف‬


‫نتعلم كيف يمكننا إدارة العمل على الكائن ‪ Dataset‬من إضافه وحذف‬
‫وغيره ‪ ،‬وكذلك اإلبحار في قاعدة البيانات وعمل فلتر الترشيح للبيانات ‪..‬‬
‫إلى أن نلتقي مرة أخرى أترككم في حفظ هللا ورعايته ‪ ..‬ياسر الشيخ‬

‫((سبحانك اللهم وبحمدك‪ ،‬أشهد أن ال إله إال أنت‪ ،‬أستغفرك وأتوب إليك ))‬

‫‪-----------------------------------------------‬‬

‫‪ ##‬إنتهاك التزامن ( ‪## ) Concurrency violation‬‬

‫نصادف أحيانا أثنا قيامنا بعملية التحديث برسائل مزعجة تسمى بإنتهاك التزامن‬
‫( ‪ ، ) Concurrency violation‬ويكون نص الرسالة كما يلي ‪:‬‬

‫" ‪Concurrency violation: the UpdateCommand affected 0 of the‬‬


‫‪"expected 1 records‬‬
‫أو‬
‫" ‪Concurrency violation: the DeleteCommand affected 0 of the expected‬‬
‫‪"1 records‬‬

‫وسوف نناقش هنا السبب والحل معاً ‪..‬‬


‫الذي يحصل عندما تضيف سجل جديد في الجدول المخزن في ‪ DataSet‬وكان‬
‫العمود المخصص للـ ‪ Primary Key‬من نوع ‪ AutoNumber‬وعند القيام بعملية‬
‫التحديث يجب أن تحصل على قيمة رقم الـ ‪ AutoNumber‬الجديدـ من الجدول‬
‫المخزن في قاعدة البيانات ‪ Access‬وإال فإنه سوف يكون هناك إختالف بين رقم‬
‫‪ Primary Key‬لكل من الجدول المخزن في ‪ Access‬والجدول المخزن في‬
‫‪ DataSet‬وحينها لو قمنا بعملية التحديث للسجل فإننا سوف نواجه فشل لعملية‬
‫التحيث ألننا باختصار تسببنا في حصول ما يسمى إنتهاك التزامن (‬
‫‪ ) Concurrency violation‬وهو يحدث لعمليات التحديث عند اإلضافة والسبب‬
‫كما أوضحنا أن قيمة رقم الـ ‪ AutoNumber‬الجديد من الجدول المخزن في قاعدة‬
‫البيانات ‪ Access‬غير متوفره للكائن ‪ DataAdapter‬الذي سوف يقوم بالتجهيز‬
‫لعملية التحديث ‪.‬‬
‫ الجديدـ من‬AutoNumber ‫ كيف نحصل على قيمة رقم الـ‬: ‫يبقى السؤال هنا‬
‫ ؟‬Access ‫الجدول المخزن في قاعدة البيانات‬
: ‫الجواب يتمثل في خطوتين‬
‫ ويتم ذلك بكتابة‬، ‫ مع األحداث التابعة له‬DataAdapter ‫ التصريح للكائن‬- ‫األولى‬
: ‫ أثناء التصريح عن المتغير كما يلي‬WithEvents ‫العبارة‬

‫كود‬
Dim WithEvents MyDataAdapter As New OleDb.OleDbDataAdapter

‫ كي ينطلق عند‬DataAdapter ‫ للكائن‬RowUpdated ‫ تجهيز الحدث‬- ‫الثانية‬


‫ الجديد من‬AutoNumber ‫القيام بعملية التحديث ويقوم بجلب قيمة رقم الـ‬
‫ ويستخدمها في اتمام عملية‬Access ‫الجدول المخزن في قاعدة البيانات‬
: ‫ وكل ذلك موجود في هذا المثال‬، ‫التحديث‬

‫كود‬
    ' Event Handler for RowUpdated Event
    Private Sub MyDataAdapter_RowUpdated(ByVal sender As Object, ByVal e As
OleDb.OleDbRowUpdatedEventArgs) Handles MyDataAdapter.RowUpdated
        If e.Status = UpdateStatus.Continue AndAlso e.StatementType =
StatementType.Insert Then
            'Get NewID
            Dim cmdGetIdentity As New OleDb.OleDbCommand("SELECT
@@IDENTITY", cn)
            cmdGetIdentity.CommandType = CommandType.Text
            ' Get the Identity column value
            e.Row("ID") = Int32.Parse(cmdGetIdentity.ExecuteScalar().ToString())
            e.Row.AcceptChanges()
        End If
    End Sub

‫وبذلك نكون قد تخلصنا من مشكلة انتهاك التزامن حيث لن تظهر عند عملية‬
‫التحديث ‪ ،‬وهللا الموفق ‪ ،‬فإن أصبت فبتوفيق هللا وحده ‪ ،‬وإن أخطأت فمن‬
‫نفسي والشيطان ‪ .‬أخوكم الفقير إلى هللا ياسر الشيخ ‪..‬‬

‫مالحظة ‪ :‬عند التطبيق يجب استبدال بعض العبارات بما يتناب مع برامجكم من‬
‫االسم الذي اخترتموه لكائن االتصال واسم العمود المخصص للـ ‪Primary Key‬‬
‫وغيره ‪ .‬وللمزيد من المعلومات أحيلكم على الرابطين التاليين ‪:‬‬

‫‪http://www.msdner.com/dev-archive/150/2-7-1501099.shtm‬‬
‫‪http://support.microsoft.com/default.aspx?...kb;en-us;815629‬‬

‫((سبحانك اللهم وبحمدك‪ ،‬أشهد أن ال إله إال أنت‪ ،‬أستغفرك وأتوب إليك ))‬

You might also like