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

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

Capture[5]

إلى هذا سطر واحد من الشفرة, كم ترى في الشفرة في الأسفل:
   1: new Operations().ExecuteQueryToList<Details>("RI_Settlements_Details_Get_BySettlement", Operations.CommandType.StoredProcedure, new Parameter("Settlement", settlement.MojID));

بعد هذا العمل الشاق لفترة تقارب العام تقريبا , كان الهدف من هذا المشروع هو  منع تكرار كتابة طبقة التواصل مع قواعد البيانات كليا و التركيز أكثر على الشفرة المطلوبة منك العمل عليها , على أية حال بعد تجربة للمشروع لأكثر من شهرين تقريبا لاحظ تكرار جزء اخر من الشفرة و هيا العمليات الاساسية التي لا تحتاج فيها إلى أي نوع من المنطق مثل عمليات الـ Create , Delete , Update  و عمليا الاستعلام Get  سواء لمجموعة من الصفوف أو لصف واحد, للنظر إلى الشفرة التالي لتصنيف Application:


   1: public class Application
   2: {
   3:     public Application Get(int id)
   4:     {
   5:         return new Operations().ExecuteQuery<Application>("Application_Get_ByID", Operations.CommandType.StoredProcedure, new Parameter("ID", id));
   6:     }
   7:     
   8:     public List<Application> Get()
   9:     {
  10:         return new Operations().ExecuteQueryToList<Application>("Application_Get");
  11:     }
  12:     

  13:     public bool Create()
  14:     {
  15:         int newApplicationID;

  16:         return new Operations().ExecuteNoneQuery<Application>("Application_Create", this, out newApplicationID);
  17:     }
  18:     
  19:     public bool Update()
  20:     {        
  21:         return new Operations().ExecuteNoneQuery<Application>("Application_Update", this);
  22:     }

  23:     

  24:     public bool Delete()
  25:     {
  26:         return new Operations().ExecuteNoneQuery<Application>("Application_Delete", this);
  27:     }
  28: }
على الرغم من أن هذه الشفرة تبسط العمل كثيرا مقارنة بشفرة التي في الاعلى إلى أنها لا تقوم بعمل بشكل جيد, بمعنى أخر عليك دوما كتابة هذه الطرق الخمسة في التصنيفات المختلفة و هي شفرة غير ضرورية و يمكن الاستغناء عنها, و هذا محور حديثي في هذه المقالة, ما اريد الوصول له بعد عملية التعديل هو التالي :

   1: public class Application : OperationBase<Application>
   2: {
   3:     
   4: }
   5:  
   6: public class SomeClass()
   7: {
   8:     new Application().Create();
   9: }

تلاحظ في الشفرة التي في الأعلى أن التصنيف Application اصبح مشتقا من التصنيف OperationBase وهو تصنيف جديد اضفته إلى مكتبة GeniusesCode.Framework.Data.SqlServer يقوم هذا التصنيف بجميع الاعمال الأساسية الخمسة حيث يستقبل التصنيف نوعا كمتغير Generaice له, ثم يقوم بتمرير هذا التصنيف إلى الطرق المختلفة لنأخذ الطريقة Get على سبيل المثال:


   1: public T Get(int id)
   2: {
   3:     return new Operations().ExecuteQuery<T>(typeof(T).Name + "_Get_ByID", Operations.CommandType.StoredProcedure, new Parameter("ID", id));
   4: }

لاحظ هنا بأن اسم الـ Stored Producer سيأخذ اسم التصنيف ثم كلمة Get_ByID و نفس الأمر مع Create و Delete و Update, الشفرة الكاملة لإضافة OperationBase:


   1: namespace GeniusesCode.Framework.Data.SQLServer
   2: {
   3:     using GeniusesCode.Helper;
   4:     using System;
   5:     using System.Collections.Generic;
   6:  
   7:     public class OperationBase<T>
   8:     {
   9:         internal enum ExecuteNoneQueryType
  10:         {
  11:             Update,
  12:             Delete
  13:         }
  14:  
  15:         public List<T> Get()
  16:         {
  17:             return new Operations().ExecuteQueryToList<T>(typeof(T).Name + "_Get")
  18:         }
  19:  
  20:         public T Get(int id)
  21:         {
  22:             return new Operations().ExecuteQuery<T>(typeof(T).Name + "_Get_ByID", Operations.CommandType.StoredProcedure, new Parameter("ID", id));
  23:         }
  24:  
  25:  
  26:         public bool Create()
  27:         {
  28:             int newObjectID = 0;
  29:             return this.Create(this, out newObjectID);
  30:         }
  31:  
  32:         public bool Create(out int newObjectID)
  33:         {
  34:             newObjectID = 0;
  35:             return this.Create(this, out newObjectID);
  36:         }
  37:  
  38:         public bool Delete()
  39:         {
  40:             return this.Delete(this);
  41:         }
  42: 
  43:         public bool Update()
  44:         {
  45:             return this.Update(this);
  46:         }
  47: 
  48:         private bool Create<T>(T objectToUpdate, out int newObjectID)
  49:         {
  50:             newObjectID = 0;
  51:  
  52:             try
  53:             {
  54:                 return new Operations().ExecuteNoneQuery<T>(objectToUpdate.GetType().Name + "_Create", objectToUpdate, out newObjectID);
  55:             }
  56:             catch (Exception ex)
  57:             {
  58:                 new Logger(this.GetType().Name, this.GetType().Namespace, "Create(T)", ex.Message, Logger.LogType.Error);
  59:             }
  60:  
  61:             return false;
  62:         }
  63: 
  64:         private bool Delete<T>(T objectToDelete)
  65:         {
  66:             return this.DoExecuteNoneQuery<T>(objectToDelete, ExecuteNoneQueryType.Delete);
  67:         }
  68:  
  69:  
  70:         private bool Update<T>(T objectToUpdate)
  71:         {
  72:             return this.DoExecuteNoneQuery<T>(objectToUpdate, ExecuteNoneQueryType.Update);
  73:         }
  74: 

  75:         private bool DoExecuteNoneQuery<T>(T objectToExecute, ExecuteNoneQueryType executeNoneQueryType)

  76:         {
  77:             string subsequentName = string.Empty;
  78:             switch (executeNoneQueryType)
  79:             {
  80:                 case ExecuteNoneQueryType.Update:
  81:                     subsequentName = "_Update";
  82:                     break;
  83:  
  84:                 case ExecuteNoneQueryType.Delete:
  85:                     subsequentName = "_Delete";
  86:                     break;
  87:             }
  88: 
  89:             return new Operations().ExecuteNoneQuery<T>(objectToExecute.GetType().Name + subsequentName, objectToExecute, Operations.CommandType.StoredProcedure);
  90:         }
  91:     }
  92: }

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

Capture

أو من خلال NuGet Manager  كما في الصورة:

Captur99e

العودة للماضي: أخبرني و لا تسألني

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

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

الشفرة قبل:

if

الشفرة في الاعلى ليست لتحكم بمفاعل طاقة نووية , إنما هي مجرد جمل If متداخلة لا أكثر و لا أقل, نحن نواجه جمل if اكثر مما نتخيل في حياتنا اليومية عند تطوير التطبيقات و السؤال الذي يطرح نفسه في الحقيقة هل نحن حقا بحاجة إلى هذه الكمية من جمل الـ if الخرافية ؟ حسنا نحن نحتاج if لتحقق من القيمة Null , و ان متغير ما يحمل فعلا القيمة الصحيحة أو أن قيمة اكبر من أخرى و هكذا.

حسنا الشفرة التي في الأعلى أتحقق في السطور الأولى من الكائن Header و خصائصه تقوم السطور التالية من جمل if بتحقق من الكائنات المختلفة مثل API و Conusmer و Method و غيرها في جمل else  يكون جواب الجمل التي في الاعلى اذا كانت غير صحيحة, الصورة في السفل:

ثمسث

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

TDA

حسنا صدق أو لا صدق ( عذرا لكن لا يهمني كثيرا :-) ) الشفرة التي في الأعلى تقوم بنفس عمل الشفرة في الجمل المتداخلة.

شرح قانون اخبرني و لا تسألني

لنفترض بأن لديك مشروع HR مثلا حيث تنص أحد المتطلبات بأن يتم ارسال بريد الكتروني لـ CEO الخاص بشركة في حال توظيف موظف براتب أعلى من 20 الف ريال , قبل اضافته لقاعدة البيانات, ربما تفكر بسرعة و يكون جوابك بشكل التالي:

   1: Employee newEmployee = new Employee();
   2: newEmployee.Salary = 25000;
   3: if(newEmployee.Salary > 20000)
   4: {
   5:     Console.WriteLine("Salary is bigger then 20000");
   6: }


   1: namespace ConsoleApplication1
   2: {
   3:     public class Employee
   4:     {
   5:         public int Salary { get; set; }
   6:     }
   7: }

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



   1: using System;
   2: namespace ConsoleApplication1
   3: {
   4:     public class Employee
   5:     {
   6:         int salary;
   7:  
   8:         public int Salary
   9:         {
  10:             get
  11:             {
  12:                 return this.salary;
  13:             }
  14:  
  15:             set
  16:             {
  17:                 if(value <= 20000)
  18:                 {
  19:  
  20:                 }
  21:                 else
  22:                 {
  23:                     Console.WriteLine("Salary is bigger then 20000");
  24:                 }
  25:             }
  26:         }
  27:     }
  28: }

لنرى الأن كيف حل هذا التغير البسيط في الشفرة



   1: Employee newEmployee = new Employee();
   2: newEmployee.Salary = 25000;   

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



   1: namespace TAMS.Model
   2: {
   3:     using System.Collections.Generic;
   4:  
   5:     public class Request
   6:     {
   7:         public Request()
   8:         {
   9:         }
  10:  
  11:         public Request(Header header, List<Parameter> parameters)
  12:         {
  13:             this.Header = header;
  14:             this.Parameters = parameters;
  15:         }
  16:         public Header Header { get; set; }
  17:  
  18:         public List<Parameter> Parameters { get; set; }
  19:     }
  20: }

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



   1: public bool VerifyRequestObject(Request request, out Response response, ref MethodRequestLogger logger)
   2: {
   3:     response = null;
   4:     if (logger == null)
   5:     {
   6:         logger = new MethodRequestLogger();
   7:     }
   8:  
   9:     try
  10:     {
  11:         string methodName = "VerifyRequestObject(Request, out Response)";
  12:         if (request != null)
  13:         {
  14:             if (request.Header != null)
  15:             {
  16:                 if (!string.IsNullOrWhiteSpace(request.Header.UserName))
  17:                 {
  18:                     if (!string.IsNullOrWhiteSpace(request.Header.Password))
  19:                     {
  20:                         if (!string.IsNullOrWhiteSpace(request.Header.OperationName))
  21:                         {
  22:                             Controller.Characters.Consumer consumer = new Controller.Characters.Consumer();                                                                     
  23:                             if (consumer.IsAuthenticate(request.Header.UserName, request.Header.Password))
  24:                             {
  25:                                 logger.IsUserAuthenticated = true;
  26:                                 Controller.API api = new Controller.API().Get(request.Header.ServiceID);
  27:                                 if (api != null)
  28:                                 {
  29:                                     Controller.Method method = new Controller.Method().Get(request.Header.OperationName, api);
  30:                                     if (method != null)
  31:                                     {
  32:                                         logger.Method = method;
  33:                                         if (!method.IsPubliclyEnabled)
  34:                                         {
  35:                                             if (!new Controller.MethodPermission().IsConsumerHasRightInThisMethod(method, consumer))
  36:                                             {
  37:                                                 response = Model.Response.CreateResponseObject("TAMS.RequestHandler", "TAMS.RequestHandler.RequestVerification", "your authentication information have no permission in this operation", methodName, 1290);
  38:                                                 return false;
  39:                                             }
  40:                                         }
  41:  
  42:                                         List<Controller.MethodParameter> methodsParameters = new Controller.MethodParameter().Gets(method);
  43:                                         if (RequestVerification.IsRequestHasAllParameters(request, out response, method, methodsParameters))
  44:                                         {
  45:                                             foreach (Controller.MethodParameter parameter in methodsParameters)
  46:                                             {
  47:                                                 if (!RequestParameterRoleVerification.ValidateParameterRole(parameter, request, out response, ref logger))
  48:                                                 {
  49:                                                     return false;
  50:                                                 }
  51:                                             }
  52:                                         }
  53:                                         else
  54:                                         {
  55:                                             return false;
  56:                                         }
  57:  
  58:                                         return RequestCreditVerification.CreditVerify(request, out response, methodName, consumer, method);
  59:                                     }
  60:                                     else
  61:                                     {
  62:                                         response = Model.Response.CreateResponseObject("TAMS.RequestHandler", "TAMS.RequestHandler.RequestVerification", "Operation Name incorrect", methodName, 1289);
  63:                                     }
  64:                                 }
  65:                                 else
  66:                                 {
  67:                                     response = Model.Response.CreateResponseObject("TAMS.RequestHandler", "TAMS.RequestHandler.RequestVerification", "Service ID Incorrect", methodName, 1288);
  68:                                 }
  69:                             }
  70:                             else
  71:                             {
  72:                                 logger.Consumer = consumer;   
  73:                                 response = consumer.Response;
  74:                                 response = Model.Response.CreateResponseObject("TAMS.RequestHandler", "TAMS.RequestHandler.RequestVerification", response.Error.Message, methodName, response.Error.ID);
  75:                                 logger.IsUserAuthenticated = false;
  76:                             }
  77:                         }
  78:                         else
  79:                         {
  80:                             response = Model.Response.CreateResponseObject("TAMS.RequestHandler", "TAMS.RequestHandler.RequestVerification", "Operation Name can't be null", methodName, 1262);
  81:                         }
  82:                     }
  83:                     else
  84:                     {
  85:                         response = Model.Response.CreateResponseObject("TAMS.RequestHandler", "TAMS.RequestHandler.RequestVerification", "Password can't be null", methodName, 1263);
  86:                     }
  87:                 }
  88:                 else
  89:                 {
  90:                     response = Model.Response.CreateResponseObject("TAMS.RequestHandler", "TAMS.RequestHandler.RequestVerification", "User Name not set", methodName, 1264);
  91:                 }
  92:             }
  93:             else
  94:             {
  95:                 response = Model.Response.CreateResponseObject("TAMS.RequestHandler", "TAMS.RequestHandler.RequestVerification", "Header can't be null", methodName, 1265);
  96:             }
  97:         }
  98:     }
  99:     catch (Exception ex)
 100:     {
 101:         response = Model.Response.CreateResponseObject("TAMS.RequestHandler", "TAMS.RequestHandler.RequestVerification", ex.Message, "VerifyRequest(Request, out Response)", 1244);
 102:     }
 103:  
 104:     return false;
 105: }

بينما اذا تم تطبيق فكرة أخبرني و لا تسألني و تم كتابة الكائن Request بهذا الشكل :



   1: namespace TAG.RequestHandler.Entities
   2: {
   3:     using System.Collections.Generic;
   4:     using TAG.Helper;
   5:  
   6:     public class Request
   7:     {
   8:         private Header header;
   9:  
  10:         public Request()
  11:         {
  12:         }
  13:  
  14:         public Request(Header header, List<Parameter> parameters)
  15:         {
  16:             this.Header = header;
  17:             this.Parameters = parameters;
  18:         }
  19:  
  20:         public Header Header
  21:         {
  22:             get
  23:             {
  24:                 if(this.header != null)
  25:                 {
  26:                     return this.header;
  27:                 }
  28:  
  29:                 throw new Error(1301, "Header is null");
  30:             }
  31:  
  32:             set
  33:             {
  34:                 if (value != null)
  35:                 {
  36:                     this.header = value;
  37:                 }
  38:                 else
  39:                 {
  40:                     throw new Error(1301, "Header is null");
  41:                 }
  42:             }
  43:         }
  44:  
  45:         public List<Parameter> Parameters { get; set; }
  46:     }
  47: }

القليل من جمل If لن يتم بسببها الوصول إلى الكائنات Consumer أو Method حتى يكون الكائن Reuqest مع جميع خصائصه صحيح 100% و اذا لم يكن صحيحا سيتولى الكائن نفسه معالجة الأمر و ليس الطريقة التي تتحقق من الكائن Request , يمكن تلخيص فكرة كل شيء يتعلق بكائن ما يجب أن يكون داخل حدود الكائن نفسه و ليس خارجة, إليك شفرات التصنيفات الثلاثة:



   1: namespace TAG.Controller.Characters
   2: {
   3:     using GeniusesCode.Framework.Data.SQLServer;
   4:     using GeniusesCode.Helper;
   5:     using System;
   6:     using TAG.Helper;
   7:  
   8:     public class Application : Model.Characters.Application
   9:     {
  10:         public Application AuthenticateAndGetUser(string userName, string password)
  11:         {
  12:             Application application = new Operations().ExecuteQuery<Application>("Application_Authenticated", Operations.CommandType.StoredProcedure, new Parameter("UserName", userName), new Parameter("Password", password));
  13:                 if (application == null)
  14:                 {
  15:                     throw new Error(1501, "Application not authenticated");
  16:                 }
  17:  
  18:                 if (application.IsApproved == false)
  19:                 {
  20:                     throw new Error(1502, "Application is not Approved");
  21:                 }
  22:  
  23:                 if (application.IsLockedOut == true)
  24:                 {
  25:                     throw new Error(1503, "Application is LockedOut");
  26:                 }
  27:  
  28:                 return application;        }
  29:     }
  30: }


   1: namespace TAG.Controller.Entities
   2: {
   3:     using GeniusesCode.Framework.Data.SQLServer;
   4:     using System;
   5:     using TAG.Helper;
   6:  
   7:     public class Service : Model.Entities.Service
   8:     {
   9:         public Service()
  10:         {
  11:         }
  12:  
  13:         public Service Get(int id)
  14:         {
  15:                Service service = new Operations().ExecuteQuery<Service>("Service_Get_ByID", Operations.CommandType.StoredProcedure, new Parameter("ID", id));
  16:                if(service == null)
  17:                {
  18:                    throw new Error(1501, "service not found");
  19:                }
  20:  
  21:                return service;
  22:         }
  23:     }
  24: }



   1: namespace TAG.Controller.Entities
   2: {
   3:     using GeniusesCode.Framework.Data.SQLServer;
   4:     using System;
   5:     using TAG.Helper;
   6:  
   7:     public class Method : TAG.Model.Entities.Method
   8:     {
   9:         public Method Get(string name, int serviceID)
  10:         {
  11:             Method method = new Operations().ExecuteQuery<Method>("Method_Get_ByNameAndService", Operations.CommandType.StoredProcedure, new Parameter("Name", name), new Parameter("Service", new Entities.Service().Get(serviceID).ID));
  12:                 if (method == null)
  13:                 {
  14:                     throw new Error(1601, "Method not found");
  15:                 }
  16:  
  17:                 return method;
  18:         }
  19:     }
  20: }


   1: namespace TAG.Controller.Entities
   2: {
   3:     using GeniusesCode.Framework.Data.SQLServer;
   4:     using System;
   5:     using TAG.Controller.Characters;
   6:     using TAG.Helper;
   7:  
   8:     public class MethodPermission : Model.Entities.MethodPermission
   9:     {
  10:         public MethodPermission()
  11:         {
  12:         }
  13:  
  14:         public MethodPermission(Method method, Application application)
  15:         {
  16:             this.Method = method;
  17:             this.Application = application;
  18:         }
  19:  
  20:         public bool IsApplicationCanExecuteThieMethod()
  21:         {
  22:                 MethodPermission methodPermission = new Operations().ExecuteQuery<MethodPermission>("MethodPermission_CheckRightForApplication", Operations.CommandType.StoredProcedure, new Parameter("Application", this.Application.ID), new Parameter("Method", this.Method.ID));
  23:                if (methodPermission != null)
  24:                {
  25:                    if(methodPermission.AllowAccess == true)
  26:                    {
  27:                        return true;
  28:                    }
  29:                }
  30:  
  31:                throw new Error(1701, "you don't have permission to access this method");                
  32:         }
  33:     }
  34: }


   1: public bool Verified(Request request)
   2: {
   3:     if(request != null)
   4:     {                
   5:         Service service = new Service().Get(request.Header.ServiceID);
   6:         Application application = new Application().AuthenticateAndGetUser(request.Header.UserName, request.Header.Password);
   7:         Method method = new Method().Get(request.Header.OperationName, service.ID);
   8:         new MethodPermission(method, application).IsApplicationCanExecuteThieMethod();
   9:         return true;
  10:     }
  11:  
  12:     throw new Error(1401, "Request is null");            
  13: }

اذا امعنت النظر في شفرة التصنيفات الثلاثة ستجد بأنها اما تعيد كائن او ترمي Excpetion , هذا يعني مثلا أنني لن اتحقق من الأ Application الا اذا كانت Service  موجودة مسبقا و هذا تماما ما تقوم به جمل if المتداخلة في بداية المقالة :-) .