فقط ارجع بيانات من فضلك , شكرا

قضيت ليلة الامس و انا أفكر في الحكمة التي تقول " الثرثار هو من تسأله عن الوقت فخبرك كيف صنعة الساعة " بطريقة أو اخرى تذكرة حجم الشفرة التي اكتبها باستخدام مكتبة GeniusesCode.Framework.Data.SqlServer

   3: namespace GeniusesCode.UnitTest
   4: {
   5:     public class Applications
   6:     {
   7:         public int ID { get; set; }
   8: 
   9:        public string Name { get; set; }
  10:  
  11:         public bool IsEnabled { get; set; }
  12:  
  13:         public DateTime CreateDate { get; set; }
  14:  
  15:         [ForeignKey]
  16:         public Merchant Merchant { get; set; }
  17:     }
  18: }
  19:  
  20: 
  21: static void Main(string[] args)
  22: {
  23:     Applications application = new Operations().ExecuteQueryToObject<Applications>("select * from Applications where ID = @ID");
  24:     Console.WriteLine(application.ID);
  25:     Console.ReadLine();
  26: }

قبل أن الشرح ما هي المشكلة في الشفرة التي في الأعلى دعوني اشرح ما احاول عملة في الطريقة Main هو الحصول على رقم الكائن Application !! و هذه المشكلة التي تؤرقني كثيرا لقد قمت بكتابة كائن و من تمريرة ثم الحصول على الخاصية كل هذا العمل من اجل الحصول على رقم التطبيق فقط !! الأمر شبيه تمام بالحكمة التي في الأعلى " انا اطلب قيمة الـ ID فقط و انت تخبرني بتفاصيل كثيرة انا في غنى عنها " هذه التفاصيل مثل الخصائص و انواعها و اسمائها و غيرها من الامور.

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

image

المتغير application لم يتم تعريفة في المشروع نهائيا كما أنه الخاصية ID ليست موجودة على الطلاق و هنا يأتي Dynamic هذه الكلمة السحرية التي سنستخدمها لمعالجة هذه المشكلة, هذه احدى المميزات الجديدة التي اعمل عليها الاصدار الجديد من مكتبة GeniusesCode.Framework.Data.SqlServer , تأتي الطريقة ExecuteQueryToDynimcObject و التي تعيد كائن من النوع dynamic , للنظر إلى الشفرة:

   1: dynamic application = new Operations().ExecuteQueryToDynamicObject("select * from applications where ID = 3460");
   2: Console.WriteLine(application.ID);
للنظر كيف يتم الامر يتحدث كل شيء بشكل طبيعي للغاية دون أدن تغير حتى نحصل على DataTable بعد الحصول على DataTable اقوم بالمرور على كل Row بداخل الجدول مع كل سطر أقوم بالمرور على كل عمود داخل هذا السطر و من اقوم بإنشاء كائن من النوع dynamic مع تمرير اسم العمود كاسم للخاصية و محتويات العمود كمحتوى لعمود ثم اقوم بإضافة كائن إلى قائمة و اخيرا عود بالعنصر الأول من القائمة الشفرة:
   1: internal static List<dynamic> ConvertDataTableToDynimcObject(DataTable table)
   2: {
   3:     if (table != null)
   4:     {
   5:         var dynamicDt = new List<dynamic>();
   6:         dynamic dyn = new ExpandoObject();
   7:         foreach (DataRow row in table.Rows)
   8:         {
   9:            foreach (DataColumn column in table.Columns)
  10:             {
  11:                 var dic = (IDictionary<string, object>)dyn;
  12:                 dic[column.ColumnName] = row[column];
  13:             }
  14:  
  15:             dynamicDt.Add(dyn);
  16:         }
  17:  
  18:         return dynamicDt;
  19:     }
  20:  
  21:     return null;
  22: }

   1: public dynamic ExecuteQueryToDynamicObject(string command, CommandType commandType = CommandType.Text, params Parameter[] paramters)
   2: {
   3:     List<dynamic> listOfDynimcObject = DataMapper.ConvertDataTableToDynimcObject(this.ExecuteQueryToDataTable(command, commandType, paramters));
   4:     if (listOfDynimcObject != null && listOfDynimcObject.Count > 0)
   5:     {
   6:         return listOfDynimcObject[0];
   7:     }
   8:  
   9:     return null;
  10: }
و بالتالي استطعت ان احصل على البيانات فقط دون الخوض في الانواع أو التفاصيل و خلافة فقط أعطني الـ ID .
image

الشفرة ذات الرائحة النتنة ( الجزء الثاني )

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

1- الطلاسم الفرعونية
   1: public int IsUserHasAccess(string userName)
   2: {
   3:     if(userName == "ALGHABBAN")
   4:     {
   5:        return 1;
   6:     }
   7:  
   8:     return 0;
   9: }

ما المشكلة في الشفرة التي في الأعلى؟ ما معنى واحد و صفر ! اعلم تمام انك ستخبرني بأنك كتبتها في التوثيق و هذا بحذ ذاته خطئ ساتحدث عنه لاحقا ,عموما هذه وحدة من النقاط و هي الرموز الفرعونية غير مفهومة إلا من قبل المطور.

الاشخاص الذي يستخدمون هذا النظام في العادة يستخدمونه بطرق  غريبة فهم على سبيل المثال يقومون بكتابة طرق للإضافة سجل معين إلى جدول في قاعدة البيانات فإذا تمت الاضافة بشكل الصحيح تعيد الطريقة 1 و اذا لم تمم تعيد 0 , قس على ذلك تسجيل الدخول مثلا اذا كانت بيانات تسجيل الدخول صحيح تعيد الطريقة 0 و اذا كانت صحيحة و لكن المستخدم غير مرخص له في الدخول تعيد الطرقة الرقم 7.

اذا ما المشكلة في الاسلوب؟

اخفاء المنطق
للنظر إلى المحادثة التالية:
ص : ما هي السورة التي توجب الجنة ؟
ي : الفجر ..
ص: –1 .

سأل ( ص ) المستخدم ( ي ) سؤال فجاوب المستخدم  ( ي ) اجابة و كانت الاجابة خاطئة جوابه ( ص ) بإجابة اكثر حماقة من اجابته ( –1 ) لماذا لا تجاوب بـ " الاجابة خاطئة " , ما احاول قوله هنا بأن المستخدم أو من يستخدم شفرتك اي مان يتوقع اجابات منطقية على الاسئلة المطروحة امامه و انت بإجابتك بـ –1 او رمز فرعوني تضرب المنطق بالحائط  كما ترى في الاعلى.

صعبة في القراءة

حاول قراءة الشفرة التي في الأعلى دون ان ترجع إلى ي توثيق يذكر ماذا ستجد , إن الامر اشبه بحل احجية, اذا لم يكن هناك توثيق او لم ترجع إلى المطور ليجيبك على تساؤلاتك.

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

كيف يتم علاج المشكلة , هنا يأتي دور Exception للنظر إلى نفس الشفرة التي في الاعلى:

   1: public bool IsUserHasAccess(string userName)
   2: {
   3:     if(userName == "ALGHABBAN")
   4:     {
   5:         return true;
   6:     }
   7:  
   8:     throw new FaultException(new FaultReason(" you don't have access to this page "),
   9:      new FaultCode("0"));
  10: }

في الشفرة التي في الاعلى يمكنك أن ترى كيف اصبح الامر اكثر وضوحا بأن المستخدم ALGHABBAN هو الوحيد الذي لديه الصلاحية للوصول إلى شيء ما , و اذا تم ادخال اي مستخدم أخر ستقوم الشفرة برمي FaultException مع الرقم 1 و تفاصيل الخطأ.

2- النصوص

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

في الشفرة التي في الاعلى اذا اتى طلب معين بتغير النص الموجود فيها من You don't have access to this page  إلى Access Denied فعليا سيكون عليك تعديل الشفرة و من ثم عمل Update للمشروع فإذا كان ويب ستقوم عنها بعمل Deplay جديد كليا , اذا لمعالجة هذه المشكلة عليك دوما عزل هذه النصوص عن الشفرة في ملفات مستقله بها, إليك الحل الذي اتبعه في شفرات هنا, فكرتي بسيط اقوم بإنشاء ملف XML حيث يحتوي هذا الملف على جميع الخطاء التي من الممكن ان تقع في المشروع كل خطئ معرف بدخله ID معين حيث سأقوم بالبحث في ملف الـ XML عن هذا الخطء و بالتالي عرضة على المستخدم , للنظر إلى ملف XML التالية:

   1: <Errors>
   2:   <Error>
   3:     <code>E013002</code>
   4:     <Description>Application is disabled</Description>
   5:   </Error>
   6:   <Error>
   7:     <code>E013001</code>
   8:     <Description>Application not found</Description>
   9:   </Error>
  10: </Errors>
من جهة C# سأقوم بكتابة شفرة بسيطة لبحث عن الخطء في هذا الملف:

   1: public static FaultException GetFaultException(string exceptionCode)
   2: {
   3:     XmlDocument doc = new XmlDocument();
   4:     doc.Load(Common.AssemblyDirectory + "\\Errors.xml");
   5:     XmlNodeList errors = doc.SelectNodes("/Errors/Error");
   6:     foreach (XmlNode error in errors)
   7:     {
   8:         if (error["code"].InnerText == exceptionCode)
   9:         {
  10:             throw new FaultException(new FaultReason(error["Description"].InnerText), new FaultCode(exceptionCode));
  11:         }
  12:     }
  13:  
  14:     throw new FaultException(new FaultReason("undefined Error"), new FaultCode("1500"));
  15: }
ثم يمكنن استدعاء الطريقة مع الشفرة التي في الاعلى بشكل مختلف , إليك الشفرة
   1: public bool IsUserHasAccess(string userName)
   2: {
   3:    if(userName == "ALGHABBAN")
   4:     {
   5:         return true;
   6:     }
   7:  
   8:     throw this.GetFaultException("E013004");
   9: }
بهذه الطرقة إذا اردت تغير نص الخطء فلن احتاج تعديل الشفرة فقط أبحث عن الخطء في ملف XML , هناك مشكلة يجب أن تلاحظها في الشفرة التي تقوم بقراءة ملف Error.xml , المشكلة في الحقيقة هي اسم هذا الملف الذي مازال يعتبر نص و يجب تغيره و هذا الحديث يقودني إلى الحديث على نقطة اخرى في النصوص و هي النصوص التي تحتاج إلى اتخاذ قرار و ما زلنا نكتبها داحل الشفرة, للنظر إلى الشفرة التالية:
   1: private string VerifyOlpIdAlias(string value)
   2: {
   3:     if (!string.IsNullOrWhiteSpace(value))
   4:     {
   5:         if (value.Length >= 6)
   6:         {
   7:             if (value.Length <= 12)
   8:             {
   9:                 if (Regex.IsMatch(value, "^[a-zA-Z0-9_@.]+$"))
  10:                 {
  11:                     return value;
  12:                 }
  13:  
  14:                 throw EntitiesException.GetFaultException("E013112");
  15:             }
  16:  
  17:             throw EntitiesException.GetFaultException("E013111");
  18:         }
  19:  
  20:         throw EntitiesException.GetFaultException("E013110");
  21:     }
  22:  
  23:     throw EntitiesException.GetFaultException("E013010");
  24: }
الشفرة في الاعلى تفترض بشكل دائما أن طول الخاصية Alias دائما يبكون اكبر من 6 حروف و انه بحد اقصى 12 حرفا ثم يجب ان يحتوي الـ alies على ارقام و حروف و فقط @ , . , _ كعلامات خاصة , المشكلة ان الشفرة تفترض بشكل دائما ان هذه الشروط ثابته و غير قابلة لتغير , لنفترض أن العميل قرر قبول حروف خاصة اخرى كيف سيكون ردك ؟ , لمعالجة هذه المشكلة انضر لهذه الشفرة:
   1: private string VerifyOlpIdAlias(string value)
   2: {
   3:     if (!string.IsNullOrWhiteSpace(value))
   4:     {
   5:         if (value.Length >= ConfigManager.GetIntegerValue("MinAliasLength"))
   6:         {
   7:             if (value.Length <= ConfigManager.GetIntegerValue("MaxAliasLength"))
   8:             {
   9:                 if (Regex.IsMatch(value, ConfigManager.GetStringValue("AliasMatchStirng")))
  10:                 {
  11:                     return value;
  12:                 }
  13:  
  14:                 throw EntitiesException.GetFaultException("E013112");
  15:             }
  16:  
  17:             throw EntitiesException.GetFaultException("E013111");
  18:         }
  19:  
  20:         throw EntitiesException.GetFaultException("E013110");
  21:     }
  22:  
  23:     throw EntitiesException.GetFaultException("E013010");
  24: }
لا تخدعك الشفرة على الرغم من أنها نفسها تقريبا إلى انها أكثر قابلية لصيانة و التعديل و التغير مستقبلا في الواقع الشفرة في الاسفل يستطيع المستخدم العادي تعديل هذه الاعدادات دون العودة للمطور على الاطلاق , اذ ان  الشفرة التي في الاسفل تقوم بحفظ هذه الاعدادات في ملف Confgiration و بتالي يمكن صناعة واجه لتعديل هذه الاعدادات , ملف Configration :
   1: <configuration>
   2:   <appSettings>
   3:     <add key="MaxAliasLength" value="12"/>
   4:     <add key="MinAliasLength" value="6"/>
   5:     <add key ="AliasMatchStirng" value="^[a-zA-Z0-9_@.]+$"/>
   6:   </appSettings>
   7: </configuration>
اذا متى ما وجهت شفرة تتخذ قرار بناء على رقم او قيمة دائما و ابدا احفظ هذه القيمة في الاعدادات و ليس داخل الشفرة, هناك قاعدة تقول بأن العميل دائما يكذب من اجل انقاص التكلفة و الوقت لذلك عندما تتعامل مع قوانين من قبل العميل عليك دائما ان تتيح الفرص لتغير و التعديل و ليس تثبيتها.