locked
Show model property name in custom validation RRS feed

  • Question

  • User1501362304 posted

    Hi,

    I have a model with few properties and I am adding custom validation on some of its properties. Everything is fine, it is validating as expected.

    Only problem I see is in custom validators I am not able to get the model properties name as we can get that with in built validators.

    Below is code sample

    [Display(Name = "Status")]
    [RequiredIfNot(1, ErrorMessage = "Status is required")]
    public short? Status { get; set; }
    public class RequiredIfNotAttribute : ValidationAttribute, IClientModelValidator
    {
      // other code removed to keep it short
    
     protected override ValidationResult IsValid(object value, ValidationContext validationContext)
            {
                ErrorMessage = ErrorMessageString;
                short currentValue = Convert.ToInt16(value);
          
                // validation logic
               bool validationPassed = false;
                if (!validationPassed)
                    return new ValidationResult(ErrorMessage);
               else
                    return ValidationResult.Success;
            }
     }

    if I use [RequiredIfNot(1, ErrorMessage = "{0} is required")] then it shows error message as it is '{0} is required' rather than 'Status is required'

    I could pass in properties's name using nameof(Status) and replace {0} with that in validator but I want to align with built in validators.

    Thanks 

    Sunday, February 28, 2021 3:28 PM

Answers

  • User1686398519 posted

    Hi vkagrawal, 

    You need to use the ModelMetadata.Name property and the ValidationContext.MemberName property to get the name of the property. Then, you need to use String.Format to convert the value of the object into a string according to the specified format, and then insert it into another string.

    The following is an example, you can refer to it.

    RequiredIfNotAttribute

        public class RequiredIfNotAttribute : ValidationAttribute, IClientModelValidator
        {
            public int v;
    
            public RequiredIfNotAttribute(int v)
            {
                this.v = v;
            }
    
            public void AddValidation(ClientModelValidationContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException(nameof(context));
                }
                context.Attributes.Add("data-val", "true");
                context.Attributes.Add("data-val-required", String.Format(ErrorMessageString,context.ModelMetadata.Name));
            }
    
            protected override ValidationResult IsValid(object value, ValidationContext validationContext)
            {
                ErrorMessage = String.Format(ErrorMessageString,validationContext.MemberName);
                short currentValue = Convert.ToInt16(value);
    
                // validation logic
                bool validationPassed = false;
                if (!validationPassed)
                    return new ValidationResult(ErrorMessage);
                else
                    return ValidationResult.Success;
            }
        }

    Model

        public class TestCustomModel
        {
            [Display(Name = "StatusTest")]
            [RequiredIfNot(1, ErrorMessage = "{0} is required")]
            public short? Status { get; set; }
        }

    Controller

        public class CustomModelController : Controller
        {
            public IActionResult Index()
            {
                return View();
            }
            [HttpPost]
            public IActionResult Test(TestCustomModel model)
            {
                if (ModelState.IsValid)
                {
    
                }
                return RedirectToAction("Index");
            }
        }

    View

    @model DailyCoreMVCDemo.Models.TestCustomModel
    <form asp-controller="CustomModel" asp-action="Test" method="post">
        <input asp-for="Status" />
        <span asp-validation-for="Status"></span>
        <input type="submit" value="test" />
    </form>
    @section scripts{
        <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
        <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
    }

    Here is the result. 

    Best Regards,

    YihuiSun

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, March 1, 2021 2:49 AM

All replies

  • User1686398519 posted

    Hi vkagrawal, 

    You need to use the ModelMetadata.Name property and the ValidationContext.MemberName property to get the name of the property. Then, you need to use String.Format to convert the value of the object into a string according to the specified format, and then insert it into another string.

    The following is an example, you can refer to it.

    RequiredIfNotAttribute

        public class RequiredIfNotAttribute : ValidationAttribute, IClientModelValidator
        {
            public int v;
    
            public RequiredIfNotAttribute(int v)
            {
                this.v = v;
            }
    
            public void AddValidation(ClientModelValidationContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException(nameof(context));
                }
                context.Attributes.Add("data-val", "true");
                context.Attributes.Add("data-val-required", String.Format(ErrorMessageString,context.ModelMetadata.Name));
            }
    
            protected override ValidationResult IsValid(object value, ValidationContext validationContext)
            {
                ErrorMessage = String.Format(ErrorMessageString,validationContext.MemberName);
                short currentValue = Convert.ToInt16(value);
    
                // validation logic
                bool validationPassed = false;
                if (!validationPassed)
                    return new ValidationResult(ErrorMessage);
                else
                    return ValidationResult.Success;
            }
        }

    Model

        public class TestCustomModel
        {
            [Display(Name = "StatusTest")]
            [RequiredIfNot(1, ErrorMessage = "{0} is required")]
            public short? Status { get; set; }
        }

    Controller

        public class CustomModelController : Controller
        {
            public IActionResult Index()
            {
                return View();
            }
            [HttpPost]
            public IActionResult Test(TestCustomModel model)
            {
                if (ModelState.IsValid)
                {
    
                }
                return RedirectToAction("Index");
            }
        }

    View

    @model DailyCoreMVCDemo.Models.TestCustomModel
    <form asp-controller="CustomModel" asp-action="Test" method="post">
        <input asp-for="Status" />
        <span asp-validation-for="Status"></span>
        <input type="submit" value="test" />
    </form>
    @section scripts{
        <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
        <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
    }

    Here is the result. 

    Best Regards,

    YihuiSun

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, March 1, 2021 2:49 AM
  • User1501362304 posted

    Hi YihuiSun, 

    Thanks for the direct and detailed explanation. It worked straight away. 
    I just made a change to get the Display attribute property (in case it can have other name than actual property name and can have spaces)

    Instead of

    String.Format(ErrorMessageString,context.ModelMetadata.Name));

    in client side code, I used 

    String.Format(ErrorMessageString, context.ModelMetadata.GetDisplayName());

    Same thing for service side validation message, instead of

    ErrorMessage = String.Format(ErrorMessageString,validationContext.MemberName);

    I used

    ErrorMessage = String.Format(ErrorMessageString, validationContext.DisplayName);

    Rest everything worked fine as expected.

    Thanks!



    Monday, March 1, 2021 6:01 PM