تعرف إلى الواصفة ValidationAttribute وطريقة استخدامها

    هناك عدة طرق للتحقق من قيم نموذج model منها

    • باستخدام IValidatableObject
    • باستخدام الواصفة ValidationAttribute
    • الطريقة الأخرى هي استخدام EnterpriseLibrary

     

     

    في هذه المقالة سنتعرف إلى كيفية استخدام الفئة ValidationAttribute

     

    باختصار التطبيق الذي سنقوم به هو تعريف واصفة UnlikeAttribute مهمتها هي التحقق من عدم التطابق (الإختلاف) بين حقلين أو خاصيتين داخل فئة. اذا كنت قد استخدمت الواصفة CompareAttribute ستدرك أن ما سنقوم به الآن هو العكس تماما لمبدأ عمل Compare. حيث أن الفئة السابقة مهمتها التحقق من التطابق وبالتالي حدوث اختلاف بين قيمتين فهذا يعني أن النموذج غير صحيح وعليه يترتب إجراء مناسب لضمان سلامة وصحة البيانات. ولكن الواصفة UnlikeAttribute التي سنقوم بإنشائها ستكون مخالفة تماما. قد تتساءل ما الفائدة من ذلك؟ ببساطة في حال كنت قد قررت إضافة إمكانية تغير كلمة المرور للمستخدم داخل أحد تطبيقاتك فربما ستقرر بعدم السماح للمستخدم باستخدام نفس كلمة المرور الحالية ككلمة مرور جديدة!

     

    تجهيز مشروع

    1. قم بتجهيز أي مشروع تريد. في هذا المثال سيتم بناء تطبيق Console Application
    2. ومن ثم قم بإضافة المكتبة System.ComponentModel.DataAnnotations
    3. قم بإنشاء فئة جديدة وسمها UnlikeAttribute

     

    الشيفرة التالية تظهر الفئة الجديدة مع التوابع التي سنحتاج إلى العمل عليها لإتمام المهمة

     

    C#

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public
     class UnlikeAttribute : ValidationAttribute
    {
       
    private string otherPropertyName;
       
    private const string DefaultErrorMessage = "{0} must be different than {1}"
    ;
       
    public UnlikeAttribute(string otherProperty) : base
    (DefaultErrorMessage)
        {
            otherPropertyName = otherProperty;
        }

        public override string FormatErrorMessage(string name)
        {

        }

        protected override ValidationResult IsValid(object value,
                  
    ValidationContext
    validationContext)
        {

        }
    }

     

    clip_image001 لاحظ أن كلا التابعين FormatErrorMessage و IsValid عبارة عن توابع معرفة داخل الفئة ValidationAttribute والآن نقوم بإعادة تعريفها داخل الفئة الجديدة

     

    وظيفة التابع FormatErrorMessage هو تهيئة رسالة الخطأ وذلك باستخدام الخاصية ErrorMessageString التابعة للفئة ValidationAttribute [1] ( سنقوم باستخدام هذه الخاصية لاحقا عند تعرف التابع)

     

    والآن لنقوم بتجهيز التابع IsValid على النحو التالي:

    C# (UnlikeAttribute)

    protected override ValidationResult IsValid(object value
             ,
    ValidationContext
    validationConte
    {
       
    PropertyInfo
    otherProperty = validationContext.ObjectInstance
            .GetType()
            .GetProperty(otherPropertyName);
       
       
    // in case no property where found

       
    if (otherProperty == null) throw new 

                    IndexOutOfRangeException($"{otherPropertyName} does not exist");
       
       
    // getting the value of the other property

        var otherPropertyValue =

          otherProperty.GetValue(validationContext.ObjectInstance);

     

        // if they are different, then the case is successful
       
    if (!Equals(value, otherPropertyValue)) return ValidationResult.Success;

           
       
    return new ValidationResult
    (
               FormatErrorMessage(validationContext.MemberName));
    }

     

    بإختصار كما تلاحظ فإنه قد تم استخدام ميزة الإنعكاس في الدوت نت للوصول إلى قيمة الخاصية otherPropertyName

     

    clip_image001[1] ValidationContext هي عبارة عن فئة تستخدم لوصف البيئة التي قد تم فيها إجراء عملية الفحص. بمعنى آخر أن ValidationContext.ObjectInstance هو عبارة عن الكائن الذي يتم إجراء عليه عملية التحقق

     

    والآن لنجهز التابع FormatErrorMessage

    C#

    public override string FormatErrorMessage(string name)
    {
       
    return string
    .Format(ErrorMessageString, name, otherPropertyName);
    }

     

    والآن قم بإنشاء نموذج يمثل تغير كلمة المرور PasswordChangeModel ، وقم بإضافة الواصفة إلى الخاصية NewPassword ومرر قيمة الخاصية OldPassword لتقوم الواصفة لاحقا بالتحقق من إختلاف كلا قيمتهما

    C#

    public class PasswordChangeModel
    {
       
    public string OldPassword { get; set; }

        [Unlike("OldPassword")]
       
    public string NewPassword { get; set
    ; }
    }

     

    clip_image002 كيفة استخدام الواصفة

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

    C#

    static void Main(string[] args)
    {
       
    var model = new PasswordChangeModel
    ();
        model.OldPassword =
    "1234"
    ;
        model.NewPassword =
    "1234"
    ;
     

        var list = new List<ValidationResult>();
       
    if(!Validator
    .TryValidateObject(model,
           
    new ValidationContext(model, null, null), list, true
    ))
        {
           
    Console.WriteLine("Some errors found"
    );
           
    foreach (var item in list) Console
    .WriteLine(item.ErrorMessage);
        }
    }

    تم استخدام الفئة Validator واستدعاء التابع TryValidateObject لتقوم بدورها وباستدعاء التابع IsValid للواصفات المرتبطة بجميع الخصائص.

     

    والآن شغل البرنامج

    clip_image003

    ناتج تنقيذ البرنامج يظهر تطابق قيمتي OldPassword و  NewPassword في حين يتوجب إختلافهما

     

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

     

    clip_image004 أمور إضافية قد تود تطبيقها

     

    clip_image005 لنظهر أسماء الخصائص الظاهرة في رسالة الخطأ

    ربما تود إظهار أسماء الخصائص الظاهر بالرسالة بطريقة أفضل للمستخدم يمكننا استخدام الواصفة DisplayNameAttribute أو DisplayAttribute ، سنقوم باستخدام الواصفة DisplayAttribute إذ أن الأولى توفر إمكانية الربط مع ملف resx في حين الأخيرة لا توفر هذه الخاصية والآن لنجري التعديلات على الفئتين PasswordChangeModel و UnlikeAttribute على النحو التالي:

    C#

    public class PasswordChangeModel
    {
        [Display(Name = "Old Password")]
       
    public string OldPassword { get; set; }

        [Display(Name = "New Password")]
        [
    Unlike("OldPassword"
    )]
       
    public string NewPassword { get; set
    ; }

    }

     

    والآن

    C#

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public class UnlikeAttribute : ValidationAttribute

    {
       
    private string otherPropertyName;
       
    private string otherPropertyDisplay;

       
    private const string DefaultErrorMessage = "{0} must be different than {1}";
       
    public UnlikeAttribute(string otherProperty) : base
    (DefaultErrorMessage)
        {
            otherPropertyName = otherProperty;
        }

        public override string FormatErrorMessage(string name)
        {
           
    return string
    .Format(ErrorMessageString, name, otherPropertyDisplay);
        }

        protected override ValidationResult IsValid(object value,

                                   ValidationContext validationContext
        {
           
    PropertyInfo
    otherProperty = validationContext.ObjectInstance
                .GetType()
                .GetProperty(otherPropertyName);
           
           
    // in case no property where found

           
    if (otherProperty == null)
               
    throw new IndexOutOfRangeException($"{otherPropertyName} does not exist"
    );
           
           
    // getting the value of the other property

           
    var otherPropertyValue =

                              otherProperty.GetValue(validationContext.ObjectInstance);

            // if they are different, then the case is successful
           
    if (!Equals(value, otherPropertyValue)) return ValidationResult.Success;

            // checks if DisplayAttribute has been used
           
    if (!IsDefined(otherProperty, typeof(DisplayAttribute
    )))
               
    return new ValidationResult
    (

                                                       FormatErrorMessage(validationContext.MemberName));

     

            // gets the DisplayAttribute.Name
           
    var displayAttribute =  (DisplayAttribute) 

                               otherProperty.GetCustomAttribute(typeof(DisplayAttribute));

           
           
    return new ValidationResult
    (FormatErrorMessage(validationContext.MemberName));
        }
    }

     

    تم تظليل التغيرات التي طرأت على الشيفرة

     

    clip_image006

    لاحظ كيف ظهرت الأسماء بناء على الواصفة NameAttribute

     

    clip_image005[1] ماذا لو أردت استخدام ملف resources.resx

    الأمر سهل جدا قم بإضافة نوع المصدر ResourceType

    clip_image007

    محتوى ملف AppResource.resx

     

    والآن عد إلى الفئة PasswordChangeModel وأضف المصدر الذي تود للواصفة Display الربط معه

    C#

    public class PasswordChangeModel 
    {
        [
    Display(Name = "OldPassword", ResourceType = typeof(AppResource)
    )]
       
    public string OldPassword { get; set
    ; }

     

        [Display(Name = "NewPassword", ResourceType = typeof(AppResource))]
        [
    Unlike("OldPassword"
    )]
       
    public string NewPassword { get; set
    ; }
    }

     

    عدل UnlikeAttribute على النحو التالي:

    C#

    protected override ValidationResult IsValid(object value,

                          ValidationContext validationConte
    {
       
    PropertyInfo
    otherProperty = validationContext.ObjectInstance
            .GetType()
            .GetProperty(otherPropertyName);
       
       
    // in case no property where found

       
    if (otherProperty == null)
           
    throw new IndexOutOfRangeException($"{otherPropertyName} does not exist"
    );
       
       
    // getting the value of the other property

       
    var otherPropertyValue = otherProperty.GetValue(validationContext.ObjectInstance);

        // if they are different, then the case is successful
       
    if (!Equals(value, otherPropertyValue)) return ValidationResult.Success;

     

        // checks if DisplayAttribute has been used
       
    if (!IsDefined(otherProperty, typeof(DisplayAttribute
    )))
           
    return new ValidationResult
    (FormatErrorMessage(validationContext.MemberName));

        // gets the DisplayAttribute.Name
       
    var
    displayAttribute =
            (
    DisplayAttribute)otherProperty.GetCustomAttribute(typeof(DisplayAttribute
    ));

     

        // checks if Resource is bound
       
    if (displayAttribute.ResourceType != null)
        {
           
    var manager = new ResourceManager
    (displayAttribute.ResourceType);
            otherPropertyDisplay = manager.GetString(otherPropertyName);
           
    return new ValidationResult
    (
                FormatErrorMessage(manager.GetString(validationContext.MemberName)));
        }

       
       
    return new ValidationResult
    (FormatErrorMessage(validationContext.MemberName));
    }

     

    clip_image008

    لاحظ قد ظهرت الأسماء كما هي مخزنة في AppResources.resx

     

    الشيفرة المصدرية: https://github.com/a-kanaan/SharedWorkspace/tree/master/UnlikeAttributeConsoleApp

     

    المصادر:

    1. https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.validationattribute.formaterrormessage(v=vs.110).aspx
    2. https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.validationcontext(v=vs.110).aspx

Post a comment or leave a trackback: Trackback URL.

اترك رد

إملأ الحقول أدناه بالمعلومات المناسبة أو إضغط على إحدى الأيقونات لتسجيل الدخول:

شعار ووردبريس.كوم

أنت تعلق بإستخدام حساب WordPress.com. تسجيل خروج   /  تغيير )

Google photo

أنت تعلق بإستخدام حساب Google. تسجيل خروج   /  تغيير )

صورة تويتر

أنت تعلق بإستخدام حساب Twitter. تسجيل خروج   /  تغيير )

Facebook photo

أنت تعلق بإستخدام حساب Facebook. تسجيل خروج   /  تغيير )

Connecting to %s

%d مدونون معجبون بهذه: