المتابعون للمدونة

الأربعاء، 8 نوفمبر 2017

مثال على العلاقة الذاتية Self-Relation


مثال على علاقة ذاتية بين الجدول ونفسه Self-Relation:
سنرى الآن مثالا على العلاقة الذاتية Self-Relation.. لفعل هذا، سننشئ مشروعا اسمه SaveTreeNodes، وهو يعرض شجرة ويسمح للمستخدم إضافة العناصر إليها، وتغيير مستوياتها، وهي وظائف تعلمنا كيف ننشئها في المشروع TreeViewSample في كتاب "من الصفر إلى الاحتراف: برمجة نماذج الويندوز"، ولن نكرر شرحها هنا.. وستجد المشروع SaveTreeNodes ضمن أمثلة هذا الكتاب.
ما نريده هو أن نسمح للمستخدم بحفظ فروع الشجرة.. لا نحتاج هنا إلى إنشاء قاعدة بيانات كاملة لمجرد حفظ بعض عناصر الشجرة، فمن الممكن أن ننشئ مجموعة بيانات خاصة Custom DataSet، ونستخدمها لحفظ العناصر في ملف XML.. وبهذا نكون قد استفدنا من قدرات مجموعة البيانات وعلاقاتها، وفي نفس الوقت سنحفظ البيانات في ملف مستقل.
لفعل هذا، أضفنا إلى المشروع SaveTreeNodes مخطط مجموعة بيانات بالطريقة المألوفة، وأسميناه TreeDataSet، وأضفنا إليه جدولا اسمه TreeNodes، وأضفنا إليه الأعمدة التالية: 

اسم العمود
نوع بياناته
وظيفته
ID
Int16
المفتاح الأساسي، وهو يعمل أيضا كترقيم تلقائي.
Text
String
يحفظ نص فرع الشجرة.
ParentID
Int16
المفتاح الفرعي، وهو يشير إلى رقم الفرع الرئيسي للفرع الحالي.

وقد أضفنا علاقة إلى مخطط الجدول، لتربط بين الحقلين ID و ParentID.
هكذا يبدو شكل المخطط.. لاحظ في الصورة كيف تخرج العلاقة من نفس الجدول وتعود إليه.



وقد أضفنا إلى النموذج نسخة من مجموعة البيانات وأسميناها TreeDs كما فعلنا في الفقرة السابقة.. وحتى لا نعقد الأمور على أنفسنا، لن نملأ مجموعة البيانات بعناصر الشجرة إلا عند ضغط زر الحفظ، فهي مجرد وعاء وسيط يتيح لنا حفظ البيانات في ملف XML.. هذا هو كود هذا الزر:

TreeDs.Clear( )

For Each Node As TreeNode In TreeView1.Nodes

        Dim R = TreeDs.TreeNodes.AddTreeNodesRow(

                        Node.Text, Nothing)

        SaveChildren(Node, R)

Next

TreeDs.WriteXml("C:\TreeNodes.Xml")

في البداية أفرغنا مجموعة البيانات من محتوياتها، ثم مررنا عبر جذور الشجرة لإضافتها إلى الجدول TreeNodes الموجود في مجوعة البيانات.. لاحظ أن مجموعة البيانات محددة النوع قد أضافت الوسيلة AddTreeNodesRow إلى الجدول، لتتيح لنا إضافة صف جديد إليه.. هذه الوسيلة تستقبل معاملين:

- نصا يمثل قيمة الحقل Text في الصف الجديد.

- كائن صف من النوع TreeDataSet.TreeNodesRow، لترسل إليه الصف الرئيسي للصف الحالي.. هذا أسهل من أن تضع بنفسك رقم الصف الرئيسي في الحقل ParentID، وهذه إحدى التسهيلات التي منحتها لك العلاقة الذاتية.. ونظرا لأن جذور الشجرة ليست لها فروع رئيسية، فسنرسل القيمة Nothing إلى هذا المعامل.. هذا سيترك الحقل ParentID فارغا.

بعد هذا، يجب أن نضيف إلى مجموعة البيانات فروع كل جذر، بكتابة إجراء اسمه SaveChildren، وهو يستقبل معاملين:

- كائن الفرع TreeNode الذي سنضيف عناصره الفرعية إلى مجموعة البيانات.

- كائن الصف TreeDataSet.TreeNodesRow الذي يعمل كصف رئيسي، للصفوف التي سنضيفها الجدول.

هذا هو كود هذا الإجراء، مع ملاحظة أنه إجراء ارتدادي Recursive يستدعي نفسه، لأن كل فرع قد يحتوي على عناصر فرعية، كل منها قد يحتوي على عناصر فرعية، وهكذا:

Sub SaveChildren(ByVal ParentNode As TreeNode,

              ByVal ParentRow As TreeDataSet.TreeNodesRow)

       For Each Node As TreeNode In ParentNode.Nodes

            Dim R = TreeDs.TreeNodes.AddTreeNodesRow(

                            Node.Text, ParentRow)

            SaveChildren(Node, R)

       Next

End Sub

وبعد وضع جميع بيانات الفروع في مجموعة البيانات، سيتم تنفيذ آخر سطر في كود ضغط زر الحفظ، وهو يستدعي الوسيلة WriteXML لحفظ محتويات مجموعة البيانات في الملف.

وهكذا نكون قد حفظنا فروع الشجرة بالكامل.. بقي إذن أن نعيد قراءتها من الملف عند ضغط زر التحميل.. لفعل هذا سنفرغ كلا من مجموعة البيانات والشجرة من محتوياتهما، ثم نقرأ بيانات الملف باستخدام الوسيلة ReadXML:

TreeDs.Clear( )

TreeView1.Nodes.Clear( )

TreeDs.ReadXml("C:\TreeNodes.Xml")

بعد هذا سننقل البيانات من مجموعة البيانات إلى الشجرة.. لفعل هذا سنضيف الجذور إلى الشجرة أولا.. نحن نعرف أن الجذر ممثل في الجدول بصف توجد في الخانة ParentID الخاصة به القيمة DbNull.. لهذا سنمر على كل الصفوف، ونستخدم الوسيلة الجاهزة IsParentIDNull التي عرفتها لنا مجموعة البيانات محددة النوع، لنرى إن كانت هذه الخانة فارغة، فإن كانت كذلك، عرفنا فرعا جديدا، ووضعنا فيه النص الموجود في الخانة Text، وأضفناه إلى الشجرة كجذر، ثم نستدعي الإجراء LoadChildren لتحميل العناصر الفرعية في هذا الجذر.. هذا هو الكود الذي يفعل هذا:

For Each R In TreeDs.TreeNodes

        If R.IsParentIDNull Then

            Dim Node = TreeView1.Nodes.Add(R.Text)

            LoadChildren(Node, R)

        End If

Next

أيضا، يجب أن يكون الإجراء LoadChildren ارتداديا Recursive يستدعي نفسه، لتحميل العناصر الفرعية لكل فرع في جميع المستويات.

ولكن كيف نعرف العناصر الفرعية؟

هذا سهل جدا، بفضل العلاقة الذاتية المعرفة في الجدول، واستخدام الوسيلة GetChildRows لمعرفة الصفوف الفرعية التابعة لأي صف رئيسي.. هذا هو كود هذا الإجراء:

Sub LoadChildren(ByVal ParentNode As TreeNode,

               ByVal ParentRow As TreeDataSet.TreeNodesRow)       

        For Each R As TreeDataSet.TreeNodesRow In

               ParentRow.GetChildRows("Self_Relation")

                   Dim Node = ParentNode.Nodes.Add(R.Text)

                   LoadChildren(Node, R)

        Next

End Sub

هذا هو كل شيء.. يمكنك الآن تجربة البرنامج، وإضافة العناصر إلى الشجرة، وحفظها، ثم استرجاعها في أي وقت.

رائعة هي العلاقة الذاتية؟.. أليس كذلك؟

 

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

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

إرسال تعليق

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

صفحة الشاعر