简体   繁体   中英

How to create a custom validation attribute with parameters consisting of model properties

I am currently trying to make a site on asp.net core. When i press the submit button, i want all the validation messages to come up if there is incorrect input.

在此处输入图像描述

For now, i have the name and the phone number validation messages working using 'asp-validation-for=Client.Name' and Client.ContactNumber. Now i want to be able to validate from the server side if the user has checked at least one of the boxes off, or filled in the 'Other' field. So i tried to see if i can make a custom attribute which would validate it but havent gotten any luck. I have tried sending in the properties of the Client class but it throws an error saying "An object reference is required for the non-static field, method, or property 'Client.'". I have also tried the top solution here CS0120: An object reference is required for the nonstatic field, method, or property 'foo' , but I think in my case those solutions are not possible.

My code for the files that i am working with are below

Code for Client.cs (model class)

public class Client
{
    //some attributes

    [Required]
    public string Name { get; set; }

    [Required]
    [DisplayName("Bookkeeping")]
    public bool Bookkeeping { get; set; }

    [Required]
    [DisplayName("Personal Income Taxation")]
    public bool Personal_Income_Taxation { get; set; }

    [Required]
    [DisplayName("Self-Employed Business Taxes")]
    public bool Self_Employed_Business_Taxes { get; set; }

    [Required]
    [DisplayName("GST/PST/WCB Returns")]
    public bool GST_PST_WCB_Returns { get; set; }

    [Required]
    [DisplayName("Tax Returns")]
    public bool Tax_Returns { get; set; }

    [Required]
    [DisplayName("Payroll Services")]
    public bool Payroll_Services { get; set; }

    [Required]
    [DisplayName("Previous Year Filings")]
    public bool Previous_Year_Filings { get; set; }

    [Required]
    [DisplayName("Govt. Requisite Form Applicaitons")]
    public bool Government_Requisite_Form_Applications { get; set; }

    public string Other { get; set; }

    [CheckboxAndOtherValidation(bookkeeping: Bookkeeping,
        personal_Income_Taxation: Personal_Income_Taxation,
        self_Employed_Business_Taxes: Self_Employed_Business_Taxes,
        gST_PST_WCB_Returns: GST_PST_WCB_Returns,
        tax_Returns: Tax_Returns,
        payroll_Services: Payroll_Services,
        previous_Year_Filings: Previous_Year_Filings,
        government_Requisite_Form_Applications: Government_Requisite_Form_Applications,
        other: Other)]
    public bool AreCheckboxesAndOtherValid { get; set; }

FreeConsultation.cshtml (view page)

 <div class="container" style="padding:30px;"> <br /> <h1 class="text-info">Create New Client</h1><br /> <form method="post"> <div class="text-danger" asp-validation-summary="ModelOnly"></div> <div class="form-group row"> <div class="col-3"> <label asp-for="Client.Name"></label> </div> <div class="col-6"> <input type="text" asp-for="Client.Name" class="form-control" /> </div> <span class="text-danger col-3" asp-validation-for="Client.Name"></span> </div> <!-- More code -->

My utility class i created for custom validation attribute - CheckboxAndOtherValidation.cs

    public class CheckboxAndOtherValidation : ValidationAttribute
{
    private readonly bool Bookkeeping;
    private readonly bool Personal_Income_Taxation;
    private readonly bool Self_Employed_Business_Taxes;
    private readonly bool GST_PST_WCB_Returns;
    private readonly bool Tax_Returns;
    private readonly bool Payroll_Services;
    private readonly bool Previous_Year_Filings;
    private readonly bool Government_Requisite_Form_Applications;
    private readonly string Other;

    public CheckboxAndOtherValidation(bool bookkeeping,
        bool personal_Income_Taxation,
        bool self_Employed_Business_Taxes,
        bool gST_PST_WCB_Returns,
        bool tax_Returns,
        bool payroll_Services,
        bool previous_Year_Filings,
        bool government_Requisite_Form_Applications,
        string other)
    {
        this.Bookkeeping = bookkeeping;
        this.Personal_Income_Taxation = personal_Income_Taxation;
        this.Self_Employed_Business_Taxes = self_Employed_Business_Taxes;
        this.GST_PST_WCB_Returns= gST_PST_WCB_Returns;
        this.Tax_Returns = tax_Returns;
        this.Payroll_Services = payroll_Services;
        this.Previous_Year_Filings = previous_Year_Filings;
        this.Government_Requisite_Form_Applications = government_Requisite_Form_Applications;
        this.Other = other;

    }

    public override bool IsValid(object value)
    {
        return base.IsValid(value);
    }
}

Is there any other way to tackle this? (Preferably with custom attribute). First time posting a question so pardon me if there is anything missing. Thanks

EDIT

I was able to make the checkbox error come from the server side with the help of a solution, but i still have the issue that when i press the submit button, the checkbox error doesn't come at the same time as the other two. By following a tutorial, i was able to show the other two errors from the client side by including this piece of code in my view which comes with the creation of an asp.net core project

 @section Scripts { <partial name="_ValidationScriptsPartial" /> }

_ValidationScriptsPartial.cshtml

 <environment include="Development"> <script src="~/Identity/lib/jquery-validation/dist/jquery.validate.js"></script> <script src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script> </environment> <environment exclude="Development"> <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js" asp-fallback-src="~/Identity/lib/jquery-validation/dist/jquery.validate.min.js" asp-fallback-test="window.jQuery && window.jQuery.validator" crossorigin="anonymous" integrity="SOME INFO"> </script> <script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.9/jquery.validate.unobtrusive.min.js" asp-fallback-src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js" asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive" crossorigin="anonymous" integrity="SOME INFO"> </script> </environment>

Some references are here

  1. Article custom-data-annotation-validation-in-mvc
  2. Source code of CompareAttribute.cs and ComparePropertyAttribute.cs
  3. 3rd-party ExpressiveAnnotations

So in a similar way, a custom ValidationAttribute can use reflection to get runtime value by its property name ( the name from declaration or argument ) to see if the property have the required value or format you need.

public class CheckboxAndOtherValidation : ValidationAttribute
{
    readonly object TRUE = true;
    string[] _alltheOtherProperty;

    public CheckboxAndOtherValidation(params string[] alltheOthersProperty)
    {
        _alltheOtherProperty = alltheOthersProperty;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (_alltheOtherProperty?.Count() > 0 != true)
        {
            return ValidationResult.Success;
        }

        var otherPropertyInfo = validationContext.ObjectType.GetProperty(nameof(Client.Other));
        if (otherPropertyInfo != null)
        {
            object otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
            if (otherPropertyValue != null && !string.IsNullOrEmpty(otherPropertyValue.ToString()))
            {
                return ValidationResult.Success;
            }
        }

        for (var i = 0; i < _alltheOtherProperty.Length; ++i)
        {
            var prop = _alltheOtherProperty[i];
            var propertyInfo = validationContext.ObjectType.GetProperty(prop);
            if (propertyInfo == null)
            {
                continue;
            }

            object propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null);
            if (Equals(TRUE, propertyValue))
            {
                return ValidationResult.Success;
            }
        }

        return new ValidationResult("Must exist at least one field is true", _alltheOtherProperty);
    }
}

Cus I choose passing property name array list as argument, now class Client could have a simplified and clearer usage like this,

public class Client
{
    [CheckboxAndOtherValidation(nameof(Bookkeeping),
        nameof(Personal_Income_Taxation),
        nameof(Self_Employed_Business_Taxes),
        nameof(GST_PST_WCB_Returns),
        nameof(Tax_Returns),
        nameof(Payroll_Services),
        nameof(Previous_Year_Filings),
        nameof(Government_Requisite_Form_Applications))]
    public bool AreCheckboxesAndOtherValid { get; set; }
}    

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM