أهم أقسام المدونة

الصفحات

الثلاثاء، 22 يناير 2019

DataTable.RowDeleting Event

يتم حذف الصف DataTable.RowDeleting:
ينطلق عند محاولة حذف أحد سجلات الجدول (قبل إتمام الحذف).. والحقيقة أن هذا الحدث قليل الفائدة، لأنه لا يمتلك خاصية لإلغاء عملية الحذف قبل وقوعها، وكان المنتظر أن يمتلك هذا الحدث الخاصية e.Cancel لتجعله مفيدا!
وكل ما تستطيع فعله فيه، هو حفظ الصف الذي سيتم حذفه في متغير احتياطي، ليمكنك التراجع عن عملية الحذف بعد هذا لو أردت.
لاحظ أنك لا تستطيع حذف الصف بنفسك من داخل هذا الحدث، فلو حاولت استخدام الوسيلة DataTable.Rows.Remove(e.Row) لحذف الصف، فسيحدث خطأ في البرنامج!
ولو أردت منع عملية الحذف بأبسط طريقة، فعليك فعل هذا من أداة العرض.. مثلا: لو وضعت زرا على النموذج للقيام بعملية الحذف، فيمكنك أن تضيف إليه الكود الذي يسأل المستخدم إن كان يريد إتمام الحذف فعلا أم لا.. أما إن كنت تستخدم جدول عرض، فعليك استخدام الأحداث الخاصة به لفعل هذا، وإلغاء عملية الحذف إن قرر المستخدم هذا.
أما لو كنت مصرا على استخدام هذا الحدث العقيم، فسيتوجب عليك كتابة بعض الكود لفعل هذا.. وقد فعلنا هذا في المشروع التعريف الجزئي لفئة جدول المؤلفين في المشروع TableAdapter، حيث استخدمنا الحدث AuthorsRowDeleting المشتق من الحدث  RowDeletingلسؤال المستخدم إن كان يريد حذف الصف، فإن قرر إلغاء العملية، قمنا بإنشاء نسخة احتياطية من جدول المؤلفين، واستخدام الوسيلة Merge لإضافة نسخة من سجلات الجدول الحالي إلى الجدول الاحتياطي:
TempTable = new AuthorsDataTable( );
TempTable.Merge(this);
ثم استخدام الحدث RowDeleted لدمج سجلات الجدول الاحتياطي بالجدول الأصلي مرة أخرى، وهذا سيعيد حالة السجلات كما كانت، بما في ذلك السجل المحذوف:
if (TempTable != null && TempTable.Count > 0) {
         this.Merge(TempTable);
         TempTable.Clear( );
}
لاحظ أن الشرط TempTable.Count > 0 هو المؤشر الذي يشعرنا بأن المستخدم رفض إتمام عملية الحذف.. ولو لم نستخدمه، فسيتم التراجع عن جميع عمليات الحذف بغض النظر عن رأي المستخدم!
والخاصية Count بالمناسبة، هي خاصية معرفة في فئة الجدول محدد النوع، وهي مجرد اختصار للكود التالي:
if (TempTable.Rows.Count > 0)
ورغم أنها تعمل بشكل صحيح، يظل بهذه الطريقة عيب خطير، وهو اضطرارنا إلى نسخ كل سجلات الجدول للمحافظة على حالة سجل واحد فقط، وهذه كارثة على الذاكرة وسرعة التنفيذ إذا كان عدد سجلات الجدول ضخما!.. وللأسف، الوسيلة Merge الخاصة بالجدول لا تقبل دمج سجل منفرد، ولا تقبل إلا كائن جدول كمعامل.. ولو أردت حل هذه المشكلة، فعليك استخدام مجموعة بيانات احتياطية، ثم استخدام الوسيلة Merge الخاصة بمجموعة البيانات، لأنها تقبل دمج مصفوفة من السجلات، ومن السهل وضع السجل المراد حذفه في مصفوفة وإرسالها إليها:
TempDs = new DsAuthorsBooks( );
TempDs.Merge(new[ ] {e.Row}); 

تحذير هام:
لا تعرف نسخة جديدة من المجموعة TempDs على مستوى فئة جدول المؤلفين.. هذا الكود غير صحيح:
DsAuthorsBooks TempDs = new DsAuthorsBooks ( );
السبب في هذا أنه سيؤدي إلى تعريف دائري يعطل البرنامج عن العمل إلى أن يدمر كل مساحة الرصة Stack المتاحة له في الذاكرة.. فعند تعريف نسخة جديدة من مجموعة البيانات DsAuthorsBooks، ستقوم بتعريف نسخة من جدول المؤلفين، التي ستقوم بتعريف نسخة احتياطية من المجموعة TempDs التي ستقوم بتعريف نسخة جديدة من جدول المؤلفين، التي ستقوم بتعريف نسخة احتياطية من المجموعة TempDs وهكذا إلى ما لا نهاية!
وهذا نفس ما سيحدث إن استخدمت جدول مؤلفين احتياطيا وعرفت نسخة جديدة منه على مستوى الفئة.
ولحل هذه المشكلة، عرف المتغير على مستوى الفئة بدون الكلمة New:
DsAuthorsBooks TempDs;
ثم ضع النسخة الجديدة في هذا المتغير في الحدث RowDeleting:
TempDs = new DsAuthorsBooks ( );
 
لكن.. لماذا لا نستخدم نرفض التغيير الذي حدث للصف المحذوف لنستعيده مباشرة بجملة كالتالية:
e.Row.RejectChanges( );
فكرة جيدة، لكن هذه الطريقة ستعمل فقط مع المؤلفين القادمين من قاعدة البيانات، أما إذا أضاف المستخدم مؤلفا، ثم قرر حذفه، ثم ضغط Cancel لعدم إتمام عملية الحذف، فستسبب الوسيلة RejectChanges خطأ، لأن السجل المضاف يفقد كل قيمه فعليا عند حذفه!!.. هذا رغم أن هذا الصف ما زال موجودا في الجدول، وتستطيع أن تحصل على رقمه بالكود التالي:
MessageBox.Show(this.Rows.IndexOf(e.Row).ToString( ));
لكن حتى لو لم يحدث هذا الخطأ، فسيؤدي إلغاء تعديلات هذا الصف المضاف إلى حذفه من الجدول، وهكذا لن تستعيد الصف في كل الأحوال!
أيضا، لا يمكنك رفض تغييرات الجدول كله:
this.RejectChanges( );
لأن هذا سيضيع كل التعديلات التي قام بها المستخدم ولم يحفظها في قاعدة البيانات، كما أنه سيعيد كل السجلات التي حذفها من قبل، وليس فقط آخر سجل محذوف!
لهذا تظل طريقة مجموعة البيانات الاحتياطية أفضل وأدق طريقة يمكن استخدامها.
واضح طبعا أن ميكروسوفت كانت ستحيل حياتنا إلى نعيم لو أضافت الخاصية e.Cancel في هذا الحدث كما هو مألوف! 

لمزيد من التفاصيل انظر فئة جدول البيانات DataTable Class 

من كتاب من الصفر إلى الاحتراف: برمجة قواعد البيانات بتقنية ADO.NET
 

ليست هناك تعليقات:

إرسال تعليق

ملحوظة: يمكن لأعضاء المدونة فقط إرسال تعليق.