المفرد و الجمع في تسمية الـكائنات و الجداول

في البداية و قبل ان تكلم هذه المقالة " لست مهتما بنظرية ايهما اصح أو خطأ و لن أخوض هذا الجدال العقيم " ما أحاول الحديث عنه في المقالة هو امر جذب انتباهي أثناء العمل مع معالج الـ Entity Data Model Wizard و هي خاصية تسمى بـ Pluralizer or Singularize حيث تمكنك هذه الخاصية من تحويل اسماء الجداول ذات اسلوب الجمع الى اسماء مفردة , مثلا لديك جدول Categroies و عند إنشاء تصنيف له من المنطقي ان يدعى Category  و ليس Categories 

sHS5Z

 

رغبت بكل شدة تنفيذ هذا الأمر ايضا على أداة Geniuses Code Generator  الخاصة بي بعد بحث وصلت إلى أن الـ Entity Frame work يستخدم فضاء اسماء يدعى System.Data.Entity.Design.PluralizationServices حيث يتواجد به تصنيف يدعى PluralizationService و الذي يستخدمه الـ Entity Framework  من أجل تحديد الكلمات المفردة و الجمع , يمكنك استخدام التصنيف بهذا الشكل :

Capture

كانت سابقا الاداة تترك الامر للمطور من أجل تحديد اسم التصنيف و بشكل تلقائي فإنها تأخذ اسم الجدول كاسم لتنصيف اما الأن فأصبح بإمكان الاداة تحويل اسماء الجداول إلى مفردات و بالتالي وفرت جهد كبير جدا في تغير اسماء الجداول, الصورة :

d

العمل مع Data Mapping مع الواجهات

عندما نقوم بكتابة التطبيقات المختلفة اي كانت هذه التطبيقات فإن جميعها تقريبا تمثل لقصة واضحة المعالم لديك قاعدة بيانات تحتوي على الجدول و لديك واجهة لتعامل مع هذه القاعدة , تحدث في المقالات السابقة في اجزاءها الست عن كيفية معالجة مشاكل تكرار الشفرة عند التعامل مع طبقة الوصول للقاعدة البيانات
تحدت المقالات التي عن كيفية التعامل مع قواعد البيانات و تقليل الشفرة حتى وصل الأمر بنا إلى التواصل مع قاعدة البيانات بسطر واحد من الشفرة قادرة على تنفيذ جمل الاستعمال و جمل الاضافة و التعديل و الحذف , مرورا بتشفير البيانات و التحقق منها و تسجيل الاخطاء و التحدث مع قواعد بيانات مختلفة كل هذا من خلال هذا السطر فقط :
Auto Data Mapping

بعد الانتهاء من معالجة قواعد البيانات نأتي للخطوة التالية و هي التعامل مع الواجهات , إذا قبل الحديث عن الحل لنتحدث عن المشكلة, المشكلة عند الحديث عن الواجهات هي نفسها عن الحديث عن قواعد البيانات حيث يجب ان تقوم بعمل Data Mapping بشكل يدوي لكل بين كائن معين و جميع الـ Controls الموجودة في الصفحة او User Control و هذا امر غير صحيح و يستهلك الكثير من الجهد و الوقت في تنفيذ امور ليس لها علاقة اصلا في صلب التطبيق الخاص بك .

لنفترض أن لدينا الكائن Category و الكائن File حيث يجب ان ينتمي File لـ Category معين باستخدام Geniuses Code من جهة طبقة الوصول لقاعدة البيانات الامر بسيط جدا لك من جهة الواجهة فالأمر احمق للغاية فعليك ان تكون بإنشاء User Control مثلا للـ Category و اخرى للـ File و من ثم ستقوم بعمل شيء من هذا القبيل :
   1: protected void Page_Load(object sender, EventArgs e)
   2:        {
   3:            File file = new File();
   4:            this.TxtFileName.Text = file.Name;
   5:            this.LblFileType.Text = file.Type;
   6:            this.DropDownFileCategory.SelectedItem = file.Category.Name;
   7:        }

تقوم الشفرة في الاعلى بعمل Data mapping للكائن File ووضع محتوياته داخل الأدوات المختلفة لدينا في الشفرة التي الأعلى TextBox و Label و DropDownList حيث يتم عملية الـ Data Mapping  لها بشكل يدوي, يتم استخدام الشفرة التي في الأعلى من أجل عرض بيانات الكائن File إلى مختلف ادوات التحكم .

على جهة الاخرى و عند الرغبة في حفظ أو اضافة كائن File جديد ستقوم بعملية عكسية بمعنى ستأخذ محتويات الأدوات المختلفة و ضعها داخل كائن الشفرة :

   1: file.Name = this.TxtFileName.Text;
   2: file.Type = this.LblFileType.Text;
   3: file.Category.Name = new category(int.Parse(this.DropDownFileCategory.SelectedValue;
   4:  

اضافة إلى ذلك أنت بحاجة دائما إلى الفصل بين عملية إن شاء كائن جديد و ذلك باستدعاء الطريقة Create و تحديث بيانات كائن متوفرة اصلا باستدعاء الكائن الطريقة Update , بمعنى أخر انت تضيع وقتا ثمينا مجددا في عملية الـ Data Mapping بين الأدوات و الكائن .

ما اريد عملة بسيط للغاية كل ما اريده فعل هو جعل عملية الـ Data Mapping  تكون تلقائية تمام بما في ذلك عملية حفظ و الاضافة المختلفة التي تتم على الكائنات اريد في نهاية الامر كتابة شفرة بالتالية عند اضافة كائن جديد :

   1: /// <summary>
   2: /// Submit KPI Object
   3: /// </summary>
   4: /// <param name="sender">send Object</param>
   5: /// <param name="e">e Object</param>
   6: protected void SubmitKPIObject_Click(object sender, EventArgs e)
   7: {
   8:     try
   9:     {
  10:         this.save();
  11:     }
  12:     catch (CustomeException ex)
  13:     {
  14:         CustomeException.Handel(ex.Message, this.LabelInformation, this.MainWindow);
  15:     }
  16:     catch (Exception ex)
  17:     {
  18:         CustomeException.Handel(ex.Message, this.LabelInformation, this.MainWindow);
  19:         PageHelper.HandelSystemError(ex);
  20:     }
  21: }
و عند تعديل كائن جديد اريد كتابة الشفرة التالية لعمل الـ Data mapping :

   1: this.KPIControl.UpdateThisObject = selectedKPI;
   2: PageHelper.MapObjectToForm(this.KPIControl.MainPanel, selectedKPI);
فقطل لا غير , لعمل ذلك يجب استخدام relocation لنبدء بالمشكلة الأولى و هي عملية اخذ محتويات اداة User Control و صناعة كائن مع تمرير المتغيرات له , الشفرة :
   2: /// Map Object To Form
   3: /// </summary>
   4: /// <typeparam name="T">type of the object you want to map</typeparam>
   5: /// <param name="panel">all control in the form</param>
   6: /// <param name="setInThisObject">Set in this Object</param>
   7: /// <returns>an Object</returns>
   8: public static T MapFormToObject<T>(Panel panel, T setInThisObject)
   9: {
  10:     PropertyInfo[] props = typeof(T).GetProperties();
  11:     foreach (PropertyInfo property in props)
  12:     {
  13:         Control propertyControl = panel.FindControl(property.Name);
  14:         if (propertyControl != null)
  15:         {
  16:             if (propertyControl is TextBox)
  17:             {
  18:                 typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { Convert.ChangeType(((TextBox)propertyControl).Text, property.PropertyType) });
  19:             }
  20:             else if (propertyControl is CheckBox)
  21:             {
  22:                 typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { Convert.ChangeType(((CheckBox)propertyControl).Checked, property.PropertyType) });
  23:             }
  24:             else if (propertyControl is DropDownList)
  25:             {
  26:                 if (property.PropertyType.Namespace == "System")
  27:                 {
  28:                     typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { Convert.ChangeType(((DropDownList)propertyControl).SelectedValue, property.PropertyType) });
  29:                 }
  30:                 else
  31:                 {
  32:                     object x = property;
  33:                     Type type = property.PropertyType;
  34:                     if (type.GetConstructor(new Type[] { typeof(int) }) != null)
  35:                     {
  36:                         object o = Activator.CreateInstance(type, int.Parse(((DropDownList)propertyControl).SelectedValue));
  37:                         typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { o });
  38:                     }
  39:                 }
  40:             }
  41:             else if (propertyControl is FileUpload)
  42:             {
  43:                 if (((FileUpload)propertyControl).FileBytes.Length > 0)
  44:                 {
  45:                     typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { ((FileUpload)propertyControl).FileBytes });
  46:                 }
  47:             }
  48:             else if (propertyControl is Editor)
  49:             {
  50:                 typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { Convert.ChangeType(((Editor)propertyControl).Content, property.PropertyType) });
  51:             }
  52:         }
  53:     }
  54:
  55:     CleanPanelControlesFromOldData(panel);
  56:     return setInThisObject;

  57: }

تقوم الشفرة التي في الاعلى بأخذ كائن و تمرير نوعه لطريقة MapFormToObject تقوم الطريقة بالمرور على جميع خصائص الكائن المرر لها و تبحث باسم كل خاصية داخل مجموعة الأدوات الموجودة ضمن Panel و عندما تجد الاداة المطابقة للاسم الخاصية تتحقق من نوع الاداة فإذ كانت على سبيل المقال TextBox ستقوم مباشرة اخذ محتويات الخاصية Text ووضعها كقيمة للخاصية المطابقة في الكائن المرر لها, إما اذا كانت DropDownList في حالة الـ Category مثال تتحقق أولا بأن نوع الخاصية Category هل هيا من فضاء الاسماء system مثلا int او bool إذا كانت كذلك تقوم مباشرة بعملية الـ Data Mapping  مستخدمة الخاصية SelectedValue للأداة DropDownList أما اذا كانت الخاصية ليس من فضاء الاسماء فتقوم باستدعاء المشيد الذي يستقبل قيمة integer و تمرير قيمة الـ Selected Value إليها قس على ذلك بقية الأدوات. اذا الشفرة التي في الأعلى حلت مشكلة الـ Data Mapping من ادوات التحكم للـ Object  بعد عملية الـ Data Mapping تقوم الشفرة بإرجاع الكائن بعد تعين خصائصه .

نأتي للجزء الأخر من المشكلة هو أخذ محتويات كائن معين و وضعها داخل الأدوات الامر ليس مختلف نهائيا فقط في شيء واحد عوضا عن المرور حول كل خاصية للكائن أقوم بالمرور حول كل اداة في الـ panel ثم ابحث عن الخاصية المطابقة لها بالاسم فإن وجدتها قمت بتمرير قيمة الأداة إلى الخاصية , الشفرة :


   1: /// <summary>
   2: /// Map Object To Form
   3: /// </summary>
   4: /// <typeparam name="T">type of the object you want to map</typeparam>
   5: /// <param name="panel">all control in the form</param>
   6: /// <param name="setInThisObject">Set in this Object</param>
   7: /// <returns>an Object</returns>
   8: public static T MapFormToObject<T>(Panel panel, T setInThisObject)
   9: {
  10:     PropertyInfo[] props = typeof(T).GetProperties();
  11:     foreach (PropertyInfo property in props)
  12:     {
  13:         Control propertyControl = panel.FindControl(property.Name);
  14:         if (propertyControl != null)
  15:         {
  16:             if (propertyControl is TextBox)
  17:             {
  18:                 typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObjec, new object[] { Convert.ChangeType(((TextBox)propertyControl).Text, property.PropertyType) });
  19:             }
  20:             else if (propertyControl is CheckBox)
  21:             {
  22:                 typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { Convert.ChangeType(((CheckBox)propertyControl).Checked, property.PropertyType) });
  23:             }
  24:             else if (propertyControl is DropDownList)
  25:             {
  26:                 if (property.PropertyType.Namespace == "System")
  27:                 {
  28:                     typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { Convert.ChangeType(((DropDownList)propertyControl).SelectedValue, property.PropertyType) });
  29:                 }
  30:                 else
  31:                 {
  32:                     object x = property;
  33:                     Type type = property.PropertyType;
  34:                     if (type.GetConstructor(new Type[] { typeof(int) }) != null)
  35:                     {
  36:                         object o = Activator.CreateInstance(type, int.Parse(((DropDownList)propertyControl).SelectedValue));
  37:                         typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { o });
  38:                     }
  39:                 }
  40:             }
  41:             else if (propertyControl is FileUpload)
  42:             {
  43:                 if (((FileUpload)propertyControl).FileBytes.Length > 0)
  44:                 {
  45:                     typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { ((FileUpload)propertyControl).FileBytes });
  46:                 }
  47:             }
  48:             else if (propertyControl is Editor)
  49:             {
  50:                 typeof(T).InvokeMember(property.Name, BindingFlags.SetProperty, null, setInThisObject, new object[] { Convert.ChangeType(((Editor)propertyControl).Content, property.PropertyType) });
  51:             }
  52:         }
  53:     }
  54:  
  55:     CleanPanelControlesFromOldData(panel);
  56:     return setInThisObject;
  57: }

نأتي الأن لعملية الحفظ سواء بالتحديث او الإضافة بما انني استخدم User Control عوضا عن استخدام صفحة asp.net عادية و ذلك من أجل استخدام هذه الاداة متى اردت داخل صفحة asp.net , لعمل ذلك قمت بإنشاء التصنيف التالي :

   1: // <copyright file="CustomeControl.cs" company="GCC">
   2: // Copyright (c) 2014 All Rights Reserved
   3: // </copyright>
   4:  
   5: namespace GeniusesCode.Helper
   6: {    
   7:     using System;
   8:     using System.Web.UI;
   9:     using System.Web.UI.WebControls;
  10:     using AjaxControlToolkit;
  11:  
  12:     /// <summary>
  13:     /// Custome Control
  14:     /// </summary>
  15:     /// <typeparam name="T">Type of the Object</typeparam>
  16:     public class CustomeControl<T> :  System.Web.UI.UserControl
  17:     {
  18:         /// <summary>
  19:         /// Gets or sets New Object ID
  20:         /// </summary>
  21:         public int NewObjectID { get; set; }
  22:  
  23:         /// <summary>
  24:         /// Gets or sets Call this Page 
  25:         /// </summary>
  26:         public Page CallThisPage { get; set; }
  27:  
  28:         /// <summary>
  29:         /// Gets or sets Bind this Object
  30:         /// </summary>
  31:         public object BindThisObject { get; set; }
  32:  
  33:         /// <summary>
  34:         /// Gets or sets Show This Window 
  35:         /// </summary>
  36:         public ModalPopupExtender ShowThisWindow { get; set; }
  37:  
  38:         /// <summary>
  39:         /// Gets the Control Window
  40:         /// </summary>
  41:         private ModalPopupExtender ControlWindow 
  42:         {
  43:             get
  44:             {
  45:                 if (this.FindControl("LabelInformation") != null)
  46:                 {
  47:                     return (ModalPopupExtender)this.FindControl("MainWindow");
  48:                 }
  49:  
  50:                 throw new Exception(" No ModalPopupExtender has MainWindow as a Name in the givne contorl");
  51:             }
  52:         }
  53:  
  54:         /// <summary>
  55:         /// Gets or sets LabelInformation
  56:         /// </summary>
  57:         private Label LabelInformation
  58:         {
  59:             get
  60:             {
  61:                 if (this.FindControl("LabelInformation") != null)
  62:                 {
  63:                     return (Label)this.FindControl("LabelInformation");
  64:                 }
  65:  
  66:                 throw new Exception(" No Label has LabelInformation as a Name in the give contorl");
  67:             }
  68:         }
  69:  
  70:         /// <summary>
  71:         /// Gets the Main Panel
  72:         /// </summary>
  73:         private Panel Panel 
  74:         {
  75:             get
  76:             {
  77:                 if (this.FindControl("MainPanel") != null)
  78:                 {
  79:                     return (Panel)this.FindControl("MainPanel");
  80:                 }
  81:  
  82:                 throw new Exception("No Panel has MainPanel Name in the givne control");
  83:             }
  84:  
  85:         }
  86: 
  87:         /// <summary>
  88:         /// Gets or sets Interlocutor you want to update
  89:         /// </summary>
  90:         public bool IsActionToUpdateObject
  91:         {
  92:             get { return (T)this.Session["UpdateThisObject"] == null ? false : true; }
  93:         }
  94:  
  95:         /// <summary>
  96:         /// Gets or sets Interlocutor you want to update
  97:         /// </summary>
  98:         public T UpdateThisObject
  99:         {
 100:             get
 101:             {
 102:                 return  (T)this.Session["UpdateThisObject"];
 103:             }
 104:  
 105:             set
 106:             {
 107:                 this.Session["UpdateThisObject"] = value;
 108:             }
 109:         }
 110:  
 111:         /// <summary>
 112:         /// Submit Interlocutor Object
 113:         /// </summary>
 114:         /// <param name="sender">send Object</param>
 115:         /// <param name="e">e Object</param>
 116:         protected bool save()
 117:         {
 118:             bool result = false;
 119:  
 120:             try
 121:             {                
 122:                 if (this.IsActionToUpdateObject)
 123:                 {                                       
 124:                     T newObject = PageHelper.MapFormToObject<T>(this.Panel, this.UpdateThisObject);
 125:                     object[] parameters = new object[] { newObject };
 126:                     result = (bool)newObject.GetType().InvokeMember("Update", System.Reflection.BindingFlags.InvokeMethod, Type.DefaultBinder, newObject, paameters);
 127:                     this.UpdateThisObject = default(T);                    
 128:  
 129:                 }
 130:                 else
 131:                 {
 132:                     T newObject = PageHelper.MapFormToObject<T>(this.Panel);
 133:                     object[] parameters = new object[] { newObject, 0 };
 134:                     result = (bool)newObject.GetType().InvokeMember("Create", System.Reflection.BindingFlags.InvokeMethod, Type.DefaultBinder, newObject, parameters);
 135:                     if(result == true)
 136:                     {
 137:                         this.NewObjectID = (int)parameters[1];
 138:                     }
 139:                 }
 140:                 
 141:                 this.DataBind();                
 142:             }
 143:             catch (CustomeException ex)
 144:             {
 145:                 CustomeException.Handel(ex.Message, this.LabelInformation, this.ControlWindow);               
 146:             }
 147:             catch (Exception ex)
 148:             {
 149:                 CustomeException.Handel(ex.Message, this.LabelInformation, this.ControlWindow);
 150:                 PageHelper.HandelSystemError(ex);
 151:             }
 152:  
 153:             return result;
 154:         }
 155:  
 156:  
 157:         /// <summary>
 158:         /// Close KPI Window Click event
 159:         /// </summary>
 160:         /// <param name="sender">sender Object</param>
 161:         /// <param name="e">e Object</param>
 162:         protected void CloseWindow()
 163:         {
 164:             try
 165:             {
 166:                 if (this.ShowThisWindow != null)
 167:                 {
 168:                     this.ShowThisWindow.Show();
 169:                     this.ControlWindow.Hide();
 170:                 }
 171:  
 172:                 PageHelper.CleanPanelControlesFromOldData(this.Panel);
 173:                 this.UpdateThisObject = default(T);
 174:             }
 175:             catch (CustomeException ex)
 176:             {
 177:                 CustomeException.Handel(ex.Message, this.LabelInformation, this.ControlWindow);
 178:             }
 179:             catch (Exception ex)
 180:             {
 181:                 PageHelper.HandelSystemError(ex);
 182:             }
 183:         }
 184:  
 185:         /// <summary>
 186:         /// Data Bind Object
 187:         /// </summary>
 188:         private new void DataBind()
 189:         {
 190:             try
 191:             {
 192:                 if (this.BindThisObject != null)
 193:                 {
 194:                     if (this.BindThisObject is GridView)
 195:                     {
 196:                         ((GridView)this.BindThisObject).DataBind();
 197:                     }
 198:                     else if (this.BindThisObject is DropDownList)
 199:                     {
 200:                         ((DropDownList)this.BindThisObject).DataBind();
 201:                         ((DropDownList)this.BindThisObject).SelectedValue = this.NewObjectID.ToString();
 202:  
 203:                         if (this.ShowThisWindow != null)
 204:                         {
 205:                             this.ShowThisWindow.Show();                           
 206:                         }
 207:                     }
 208:                 }
 209:                 else if (this.Page != null)
 210:                 {
 211:                     Page.DataBind();
 212:                 }
 213:             }
 214:             catch (CustomeException ex)
 215:             {
 216:                 CustomeException.Handel(ex.Message, this.LabelInformation, this.ControlWindow);
 217:             }            
 218:         }
 219:     }
 220: }
يشتق التصنيف من User Control مع تمرير نوع الكائن الخاص بي الأداة هناك بعض الخائص الخاصة بي حيث انني استخدم اداة ModalPopupExtender لعرض شاشة على المستخدم داخل الصفحة الحالية بدلا عن استخدام صفحة asp.net  تقليدية, هناك الخاصية UpdateThisObject و التي يتم تعينها بالكائن المراد تحديثه عند التحديث و ايضا الطريقة Save حيث تقوم هذه الاخيرة بتحقق اولا هل الخاصية IsActionToUpdateObject و التي بدورها تحقق من ان UpdateThisObject فارغة فإذا كانت فارغة هذا يعني ان المستخدم يحاول إضافة كائن جديد عنها سأستدعي الطريقة Create لهذا الكائن ممرا نتيجة التي حصلت عليها من الطريقة MapFormToObject , اما اذا كان الامر لتحديث كائن فسأقوم باستدعاء الطريقة MapObjectToObject ممرا لها الكائن الذي احاول تعديله , يمكن استخدام الشفرة التي في الاعلى مع User Control بالشكل التالي :
   1: //-----------------------------------------------------------------------
   2: // <copyright file="KPI.ascx.cs" company="Geniuses Code">
   3: //     Copyright (c) Geniuses Code. All rights reserved.
   4: // </copyright>
   5: //-----------------------------------------------------------------------
   6:  
   7: namespace GKPI.WebUI.ObjectControls
   8: {
   9:     using System;
  10:     using System.Web.UI.WebControls;
  11:     using AjaxControlToolkit;
  12:     using GeniusesCode.Helper;
  13:  
  14:     /// <summary>
  15:     /// KPI Control
  16:     /// </summary>
  17:     public partial class KPI : GeniusesCode.Helper.CustomeControl<GKPI.Controller.KPI>
  18:     {
  19:         /// <summary>
  20:         /// Submit KPI Object
  21:         /// </summary>
  22:         /// <param name="sender">send Object</param>
  23:         /// <param name="e">e Object</param>
  24:         protected void SubmitKPIObject_Click(object sender, EventArgs e)
  25:         {
  26:             try
  27:             {
  28:                 this.save();
  29:             }
  30:             catch (CustomeException ex)
  31:             {
  32:                 CustomeException.Handel(ex.Message, this.LabelInformation, this.MainWindow);
  33:             }
  34:             catch (Exception ex)
  35:             {
  36:                 CustomeException.Handel(ex.Message, this.LabelInformation, this.MainWindow);
  37:                 PageHelper.HandelSystemError(ex);
  38:             }
  39:         }
  40:  
  41:         /// <summary>
  42:         /// Close KPI Window Click event
  43:         /// </summary>
  44:         /// <param name="sender">sender Object</param>
  45:         /// <param name="e">e Object</param>
  46:         protected void CloseKPIWindow_Click(object sender, EventArgs e)
  47:         {
  48:             this.CloseWindow();
  49:         }
  50:     }
  51: }

لاحظ بأن KPI هيا اداة User Control مشتقة من CustomeControl مع تمرير KPI كنوع لهذه الاداة , تحتوي هذه الاداة على زر واحد فقط من اجل الحفظ و الذي يستدعي الطريقة Save التي في الأعلى و التي ستتولى مهمة تحديد ما ذا كان الكائن لتعديل او احذف .

من صفحة التي تعرض قائمة بـ KPI اقوم الكائن المحدد للخاصية UpdateThisObject بالشكل التالي :
   1: /// <summary>
   2: /// KPI Grid Row Command
   3: /// </summary>
   4: /// <param name="sender">sender Object</param>
   5: /// <param name="e">e Object</param>
   6: protected void KPIGrid_RowCommand(object sender, GridViewCommandEventArgs e)
   7: {
   8:     try
   9:     {
  10:         if (!string.IsNullOrEmpty(e.CommandName))
  11:         {
  12:             int kpiID = 0;
  13:             if (int.TryParse(e.CommandArgument.ToString(), out kpiID))
  14:             {
  15:                 Controller.KPI selectedKPI = new Controller.KPI().Get(kpiID);
  16:                 switch (e.CommandName)
  17:                 {
  18:                     case "UpdateKPI":
  19:                         this.KPIControl.KPIWindowTitle.Text = " تعديل :" + selectedKPI.Code;
  20:                         this.KPIControl.UpdateThisObject = selectedKPI;
  21:                         PageHelper.MapObjectToForm(this.KPIControl.MainPanel, selectedKPI);
  22:                         this.KPIControl.MainWindow.Show();
  23:                         break;
  24:                     case "DeleteKPI":
  25:                         selectedKPI.Delete();
  26:                         this.DataBind();
  27:                         break;
  28:                 }
  29:             }
  30:         }
  31:     }
  32:     catch (CustomeException ex)
  33:     {
  34:         CustomeException.Handel(ex.Message, this.KPIControl.LabelInformation, this.KPIControl.MainWindow);
  35:     }
  36:     catch (Exception ex)
  37:     {
  38:         PageHelper.HandelSystemError(ex, this.ErrorControl.TextError, this.ErrorControl.ErrorWindow);
  39:     }
  40: }

و بالتالي اختصرت العملية كلها في سطرين فقط هما Save و MapObjectToForm .