العمل مع قواعد البيانات مجددا ( الجزء الرابع )

تحديث في المقالة السابقة عن اداة Geniuses Code لتوليد الشفرة , بعد اسبوعين تقريبا من العمل المتواصل استطعت تطوير هذه الأداة حتى اصبحت أكثر عملية , النسخة السابقة منها كانت تكتفي فقط بتوليد شفرة الـ Model على شكل ملفات C Sharp و كان يجب على المطور ان يقوم بنسخ او استيراد هذه الملفات إلى المشروع الخاص به .

اما بعد التحديث الجديدة تقوم الاداة الأن بتوليد شفرة لمشروع كاملين لـ Controller و Model و تحت مشروع C Sharp واحد مع استدعاء الـ DLL الخاص بـ Geniuses Code Frame work Data بالإضافة إلى بناء الـ Model و اضافة بشكل تلقائي إلى مشروع الـ Controller و بالتالي الاستغناء عن عملية اضافة الـ References في كل مرة , شاهد الفيديو ثم لنا حديث اخر :


تعتبر عملية كتابة الـ Model  و الـ Controller أحد أهم الخطوات التي يتم تكرراها مرارا و تكرارا مع كل تطبيق تقوم بالعمل عليه, لذلك لا يوجد أسهل من أن تقوم ببناء أداة تقوم بهذا العمل .


إن اول مشكلة واجهتني هي كتابة شفرة الـ Model  و عمل Compiler لها بحيث ينتج عنها DLL استطيع إضافته كـ Reference لمشروع الـ Controller  , بعد عملية كنت على موعد مع CodeDom , حيث يسمح لك هذا التصنيف بإنشاء شفرة مصدرية وترجمتها أثناء تشغيل التطبيق , الشفرة :
   1: public static void BuildDLL(string path, string code, string geniusesCodePath)
   2: {
   3:             System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
   4:             parameters.GenerateExecutable = false;
   5:             parameters.OutputAssembly = path;
   6:             parameters.ReferencedAssemblies.Add(geniusesCodePath);
   7:             CompilerResults r = CodeDomProvider.CreateProvider("CSharp").CompileAssemblyFromSource(parameters, code);
   8:             foreach (CompilerError CompErr in r.Errors)
   9:             {
  10:                 throw new Exception("Line number " + CompErr.Line +
  11:                 ", Error Number: " + CompErr.ErrorNumber +
  12:                 ", '" + CompErr.ErrorText + ";" +
  13:                 Environment.NewLine + Environment.NewLine);
  14:             }
  15:         }

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

   1:  
   2: Microsoft Visual Studio Solution File, Format Version 12.00
   3: # Visual Studio 2013
   4: VisualStudioVersion = 12.0.30501.0
   5: MinimumVisualStudioVersion = 10.0.40219.1
   6: Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeniusesCode.Framework.DataBase", "GeniusesCode.Framework.DataBase\GeniusesCode.Framework.DataBase.csproj", "{E5B2B657-2A21-4630-80FF-4710CF0172F9}"
   7: EndProject
   8: Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeniusesCode.CodeGenerator", "GeniusesCode.CodeGenerator\GeniusesCode.CodeGenerator.csproj", "{FFB54CAF-E8B1-40BD-AFBC-FEFA9C3A4A0E}"
   9: EndProject
  10: Global
  11:     GlobalSection(TeamFoundationVersionControl) = preSolution
  12:         SccNumberOfProjects = 3
  13:         SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
  14:         SccTeamFoundationServer = https://alghabban.visualstudio.com/defaultcollection
  15:         SccLocalPath0 = .
  16:         SccProjectUniqueName1 = GeniusesCode.Framework.DataBase\\GeniusesCode.Framework.DataBase.csproj
  17:         SccProjectName1 = GeniusesCode.Framework.DataBase
  18:         SccLocalPath1 = GeniusesCode.Framework.DataBase
  19:         SccProjectUniqueName2 = GeniusesCode.CodeGenerator\\GeniusesCode.CodeGenerator.csproj
  20:         SccProjectName2 = GeniusesCode.CodeGenerator
  21:         SccLocalPath2 = GeniusesCode.CodeGenerator
  22:     EndGlobalSection
  23:     GlobalSection(SolutionConfigurationPlatforms) = preSolution
  24:         Debug|Any CPU = Debug|Any CPU
  25:         Release|Any CPU = Release|Any CPU
  26:     EndGlobalSection
  27:     GlobalSection(ProjectConfigurationPlatforms) = postSolution
  28:         {E5B2B657-2A21-4630-80FF-4710CF0172F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  29:         {E5B2B657-2A21-4630-80FF-4710CF0172F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
  30:         {E5B2B657-2A21-4630-80FF-4710CF0172F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
  31:         {E5B2B657-2A21-4630-80FF-4710CF0172F9}.Release|Any CPU.Build.0 = Release|Any CPU

  32:         {FFB54CAF-E8B1-40BD-AFBC-FEFA9C3A4A0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  33:         {FFB54CAF-E8B1-40BD-AFBC-FEFA9C3A4A0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
  34:         {FFB54CAF-E8B1-40BD-AFBC-FEFA9C3A4A0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
  35:         {FFB54CAF-E8B1-40BD-AFBC-FEFA9C3A4A0E}.Release|Any CPU.Build.0 = Release|Any CPU
  36:     EndGlobalSection
  37:     GlobalSection(SolutionProperties) = preSolution
  38:         HideSolutionNode = FALSE
  39:     EndGlobalSection
  40: EndGlobal

إذا كل ما علي هو توليد شفرة تقوم بكتابة ملفات تحمل نفس الصيغة و ذات امتداد SLN او csproject اذا كان مشروع C# و بعد شيء من التحقق توصلت إلى الية كتابة هذا الملفات و كيفية ربطها ببعض.

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

Capture

العمل مع قواعد البيانات مجددا ( الجزء الثالث )

مرحبا مجددا , تحدث في المقالة السابقة عن أحد أكثر الاجزاء التي تتكرر مرار و تكرارا أثناء التعامل مع قواعد,  حيث كان ذلك الجزء هو أحد أكثر الاجزاء المرهقة طبعا بعد كتابة الـ Model و الاستغناء عن التعامل المباشر مع ADO.net و استبدالها بتقنيات Geniuses Code. سهلت فكرة استخدام مكتبة Geniuses Code  العمل حتى اللحظة باختصار الكثير من الشفرات و تبسيط العملية و تسهليها حتى اصبح تقريبا الأمر يتم بسطر واحد فقط من الشفرة.
بعد أن قمت بكاتبة المقالة على Code Project كان أحد التعليقات :
Capture 
تذكرت حينها تجربتي السابقة في أحد المشاريع مع أداة oxygencode و كيف كانت اداة عظيمة في ذلك الوقت على الرغم من أن الشفرة التي تقوم بتوليدها معقدة نوعا مع و مليئة بالأخطاء خصوصا فيما يتعلق بتحويل انوع البيانات , كنت أطر في كثير من الأحيان إلى الاستغناء عن شفرة Oxygencode و استبدالها بشفرة خاصة بي. 
إلى أن التعليق الذي ترك على المقالة شد انتباهي كثيرا, اعلم أنه موجود و لكني لم أقم بعمل شيء مثل هذا من قبل , لذلك قمت بجمع أكبر قدر ممكن من المعلومات و بدأت بالعمل , بدأت بتجميع أفكاري ماذا اريد ا افعل :
  • بناء اداة تقوم بتوليد شفرة طبقة الـ Model .
  • بناء اداة تقوم بتوليد شفرة طبقة الـ Controller .
بحيث تحقق الاداة الشروط التالية :
  • يجب أن تستخدم مكتبة Geniuses Code .
  • يجب أن يتم توليد شفرة متوافقة مع style Cop .
  • يجب أن أمنح المطور العديد من الخصائص من أجل تعديل خصائص التحقق التلقائي و اسماء الحقول و تعين الخصائص ذات المفتاح الرئيسي و الخصائص ذات المفتاح الأجنبي و الخصائص التي يكون نوعها كائن أخر.
  • أن تحافظ هذه الأداة على البساطة المتبعة سواء في الشفرة المولدة أو في واجهة التطبيق نفسه.
بعد عمل متواصل لمدة خمسة أيام تقريبا قمت ببناء أداة Geniuses Code Generator  و التي يمكنك تحميلها من هنا , حسنا لنتحدث عن التفاصيل :
Capture2

الخطوة الأولى  إنشاء طبقة الـ Model :
لإنشاء الـ Model يجب على التطبيق ان يتعرف على قائمة الجداول و جميع الحقول الخاصة و لعمل ذلك أنقر على ايقونة قاعدة البيانات في نفاذة Tables and Project , ستظهر لك نافذة نص الاتصال بقاعدة البيانات كما في الصورة :

Capture3
Capture4
الأن اضغط على Save و سوف يتم التعرف على جميع الجداول الموجودة في قاعدة البيانات الخاصة بك كما ترى في الصورة التي على اليمين , أذا قمت بضغط على Double Click  على اي جدول من الجداول الخاصة بقاعدة البيانات , سوف يتم مباشرة توليد الـ Model الخاص بهذا الجدول , الصورة في الأسفل :

Capture5
Capture6إذا الخطوة الاولى في الاتصال بقاعدة البيانات و جلب الجداول و كتابة الـ Model الخاص بجدول تمة بنجاح نأتي الأن للخطوة التالية و هي عملية تخصيص الشفرة التي تم توليدها من ناحية الـ Priamry key و الـ Auto validations  و لعمل ذلك ياقتي دور شاشة الـ proprties  , فبمجر تحديد الجدول من قائمة الجداول سوف يتم مباشرة تحديد الخصائص المتعلقة بهذا الجدول على شاشة الـ Proprties  على يسار .
تستطيع من هذه الشاشة تعديل خصائص مثل أسم التصنيف للجدول ( الخاصية Class Name ) وذلك في حالة كان اسم التصنيف مختلفا عن اسم الجدول مثل عند استخدامك للـ _ او اسماء جداول كبيرة أي كان الاسلوب الذي تتبعه في تسمية الجداول.
في الصورة التي في الاعلى مثلا لدينا الجدول الـ Categories  و اريد اسم تغير اسم التصنيف إلى Category فكل ما علي عمله هنا هو تغير الخاصية Class Name إلى category .
بعد التغير يمكنك الضغط على Double Click او على زر ?> ليتم تحديث الخصائص التي على هذا الجدول , اذا قمت بضغط على الخاصية Colums سوف يتم تحميل نافذة بها جميع الحقول الخاصة بهذا الجدول , حيث يمنك من هنا تفعيل خصائص التحقق و تعديل المسميات و خلافه . الصورة في الأسفل
Capture7

كما يمكنك إعادة ترتيب الخصائص كما يحلوا لك و كيف تريدها ان تظهر في التصنيف , قبل الأخير تعديل خصائص المشروع يمكنك تعديل خصائص المشروع من خلال ايقونة المشروع الموجودة بجوار ايقونة قاعدة البيانات , يمكنك تعديل اسم الشركة و اسم المشروع و اسم فضاء الاسماء و اين تريد حفظ التصنيفات التي ستخرج عن الأداة , اضغط على الايقونة ثم توجه لشاشة الخصائص لتعديل هذه الخصائص.
Capture8اخيرا يمكنك استخراج جميع الملفات الخاصة بالمشروع عن طريق الضغط على أخر زر في شاشة الـ Tables and Project  تأكد من كان حفظ الملفات , كما ترى في الاسفل الـ Model الأن جاهز للعمل فقط بقم باستيراد الملفات إلى المشروع الخاص بك , الصورة في الأسفل .
أخيرا لا تنسى تثبيت GeniusesCode.Framework.Data من أجل العمل مع الشفرة التي توليدها , و ذلك عن طريق Nuget .

يمكنك تحميل Geniuses Code Generator  من هنا. 

العمل مع قواعد البيانات مجددا ( الجزء الثاني )

حسنا تحدثنا في المقالة السابقة " العمل مع قواعد البيانات مجددا " كيف أنه تم الاستغناء عن أفكار كثيرة وتبسيط الشفرة إلى ابعد حد ممكن للعمل مع قواعد البيانات، حتى وصل الأمر إلى تبسيط الشفرة بسطر واحد على الشكل التالي:
   1: public bool Create(User user)
   2: {
   3:     return new Operations().ExecuteNoneQuery<User>("UMS_users_Create", user);
   4: }

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

   1: public bool Create(User user)
   2: {
   3:     if(user != null)
   4:     {
   5:         if(!string.IsNullOrWhiteSpace(user.Name))
   6:         {
   7:             if (!string.IsNullOrWhiteSpace(user.Password))
   8:             {
   9:                 if (this.ValidatPassword(user.Password))
  10:                 {
  11:                     if (!string.IsNullOrWhiteSpace(user.Email))
  12:                     {
  13:                        if(this.ValidatEmail(user.Email))
  14:                        {
  15:                            if (user.Role != null)
  16:                            {
  17:                                if(new Role().Get(user.Role.ID) != null)
  18:                                {
  19:                                    return new Operations().ExecuteNoneQuery<User>("UMS_users_Create", user);
  20:                                }
  21:  
  22:                                throw new Exception(" الصلاحيات الممنوحة غير موجودة ");
  23:                            }
  24:                        }
  25:                     }
  26:  
  27:                     throw new Exception("البريد الكتروني لا يمكن أن يكون فاراغا");
  28:                 }
  29:             }
  30:  
  31:             throw new Exception("كلمة المرور لا يمكن أن يكون فاراغا");
  32:         }
  33:  
  34:         throw new Exception("اسم المستخدم لا يمكن أن يكون فارغا ");
  35:     }
  36:  
  37:     throw new Exception(" كائن المستخدم لا يمكن أن يكونت فارغا ");
  38: }
و كلما كتبت شفرة أكثر اتضح لي بأنني أعيد كتابة نفس الأفكار مرارا و تكرار , و هي ثلاثة أنوع من التحقق تقريبا الأول بأن الكائن ليس Null , الثاني بأن خصائص الكائن جميعها صحيح و غير فارغة إذا كانت مثلا string , أخيرا يجب عليا دائما التحقق بأن جميع الكائنات الفرعية مثل Role في مثالنا السابق صحيحة و موجودة في قاعدة البيانات و أن هناك فعلا يوجد Role لها الرقم 1 على سبيل المثال.

لنبدأ بالعمل, حسنا إن أول أمر يجب عليا أن اخبر بـ GeniusesCode هو أن الخاصية Name التابعة للكائن User لا يمكن أن تكون فارغة و أن الخاصية Password و Email لا يمكن ان تكون فارغة و يجب أن محتواها متوافق معا معايير محددة أخير يجب أن أخير أيضا GeniusesCode بأن الخاصية Role لا يمكن أن تكون فارغة و أنها أيضا لا بد أن تكون موجودة في قاعدة البيانات , لعمل هذا الأمر يجب علينا استخدام Attribute ما قمت بعمله هو بناء تصنيف اسميته AutoValidation و هو مشتق من الـ Attribute , الشفرة :
   1: // -----------------------------------------------------------------------
   2: // <copyright file="AutoValidation.cs" company="Geniuses Code">
   3: // No part of this website or any of its 
   4: // contents may be reproduced, copied,
   5: // modified or adapted, without the prior 
   6: // written consent of the author, 
   7: // </copyright>
   8: // -----------------------------------------------------------------------
   9: 
  11: namespace GeniusesCode.Framework.Data
  12: {
  13:     using System;
  14: 
  15:     /// <summary>
  16:     /// Auto Validation Class
  17:     /// </summary>
  18:     public class AutoValidation : Attribute
  19:     {
  20:         /// <summary>
  21:         /// Gets or sets ValidationType
  22:         /// </summary>
  23:         public enum ValidationType
  24:         {
  25:             Number,
  26:             Custom,
  27:             DataBaseExistence,
  28:         }        
  29:  
  30:         /// <summary>
  31:         /// Gets or sets All Null
  32:         /// </summary>
  33:         public bool AllowNull { get; set; }
  34: 
  35:         /// <summary>
  36:         /// Gets or sets Expression Object
  37:         /// </summary>
  38:         public string Expression { get; set; }
  39:  
  40:         /// <summary>
  41:         /// Gets or set Message
  42:         /// </summary>
  43:         public string ErrorMessage { get; set; }
  44:  
  45:         /// <summary>
  46:         /// 
  47:         /// </summary>
  48:         public AutoValidation.ValidationType Type { get; set; }
  49:     }
  50: }

الأن أستطيع العودة للكائن User و استخدام الـ AutoValidation بشكل التالي :
   1: [AutoValidation(AllowNull = false, ErrorMessage = "اسم المستخدم لا يمكن أن يكون فارغا")]

   2: public string Name { get; set; }

أما عن الخاصية Email و Password فستكون بالشكل التالي :

   1: [AutoValidation(
   2:     AllowNull = false, Type= AutoValidation.ValidationType.Custom,
   3:     ErrorMessage = "البريد الكتروني لا يمكن أن يكون فارغا و لبد أن يكون بهذا الشكل user@domain ",
   4:     Expression = @"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$")]

   5: public string Email { get; set; }
   6:  
   7:  [AutoValidation(
   8:     AllowNull = false, Type= AutoValidation.ValidationType.Custom,
   9:     ErrorMessage = "كلمة المرور لا يمكن أن تكون فارغة و يجب أن تحتوي على حروف و ارقام و رموز خاصة",

  10:     Expression = @"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$")]
  11: public string Password { get; set; }

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

   1: [DataBaseExistence]
   2: public Role Get(int id)
   3: {
   4:   return new Operations().ExecuteQueryToObject<Role>("UMS_Roles_Get_ByID", Operations.CommandType.StoredProcedure, new Parameter("ID", id));
   5: }
   6:  
   7:  
أخيرا يجب أن تخبر Geniuses Code بأن الخاصية Role التابعة للكائن User لا يمكن أن تكون فارغة و انها يجب أن تكون موجودة في قاعدة البيانات , الشفرة :

   1: [AutoValidation(AllowNull = false,
   2: ErrorMessage = "الصلاحيات لا يمكن ان تكون فارغة , و يجب أن تكون فاعدة البيانات",
   3: Type = AutoValidation.ValidationType.DataBaseExistence)]
   4: [ForeignKey]
   5: public Role Role { get; set; }
أخر شيء يجب أن تقوم بعمله خو اضافة الخاصية AutoValidationو جعل قيمتها true في الـ Web.Config أو App.Config الشفرة :

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:   <appSettings>
   4:     <add key="DbConnection" value="Smoething"/>
   5:     <add key="AutoValidation" value="True"/>
   6:   </appSettings>
   7:     <startup> 
   8:         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
   9:     </startup>
  10: </configuration>

إذا عدنا الأن غلى الطريقة Create التابعة للكائن User يستم اختصارها بشكل التالي :

   1: public bool Create(User user)
   2: {
   3:     return new Operations().ExecuteNoneQuery<User>("UMS_users_Create", user);
   4: }

الأن لنأتي لتحربة :

Capture5CaptureCapture2Capture3

أخيرا يمكنك استخدام و تثبيت GeniusesCode  عن طريق Nuget كتالي :
Capture[10]