How to validate one field related to another's value in ASP .NET MVC 3

前端 未结 3 398
隐瞒了意图╮
隐瞒了意图╮ 2020-11-28 07:40

I had two fields some thing like phone number and mobile number. Some thing like..

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

    [Required]
    p         


        
相关标签:
3条回答
  • 2020-11-28 08:10

    One possibility is to write a custom validation attribute:

    public class RequiredIfOtherFieldIsNullAttribute : ValidationAttribute, IClientValidatable
    {
        private readonly string _otherProperty;
        public RequiredIfOtherFieldIsNullAttribute(string otherProperty)
        {
            _otherProperty = otherProperty;
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var property = validationContext.ObjectType.GetProperty(_otherProperty);
            if (property == null)
            {
                return new ValidationResult(string.Format(
                    CultureInfo.CurrentCulture, 
                    "Unknown property {0}", 
                    new[] { _otherProperty }
                ));
            }
            var otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
    
            if (otherPropertyValue == null || otherPropertyValue as string == string.Empty)
            {
                if (value == null || value as string == string.Empty)
                {
                    return new ValidationResult(string.Format(
                        CultureInfo.CurrentCulture,
                        FormatErrorMessage(validationContext.DisplayName),
                        new[] { _otherProperty }
                    ));
                }
            }
    
            return null;
        }
    
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
                ValidationType = "requiredif",
            };
            rule.ValidationParameters.Add("other", _otherProperty);
            yield return rule;
        }
    }
    

    which you would apply to one of the properties of your view model:

    public class MyViewModel
    {
        [RequiredIfOtherFieldIsNull("Mobile")]
        public string Phone { get; set; }
    
        public string Mobile { get; set; }
    }
    

    then you could have a controller:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new MyViewModel());
        }
    
        [HttpPost]
        public ActionResult Index(MyViewModel model)
        {
            return View(model);
        }
    }
    

    and finally a view in which you will register an adapter to wire the client side validation for this custom rule:

    @model MyViewModel
    
    <script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
    <script type="text/javascript">
        jQuery.validator.unobtrusive.adapters.add(
            'requiredif', ['other'], function (options) {
    
                var getModelPrefix = function (fieldName) {
                    return fieldName.substr(0, fieldName.lastIndexOf('.') + 1);
                }
    
                var appendModelPrefix = function (value, prefix) {
                    if (value.indexOf('*.') === 0) {
                        value = value.replace('*.', prefix);
                    }
                    return value;
                }
    
                var prefix = getModelPrefix(options.element.name),
                    other = options.params.other,
                    fullOtherName = appendModelPrefix(other, prefix),
                    element = $(options.form).find(':input[name="' + fullOtherName + '"]')[0];
    
                options.rules['requiredif'] = element;
                if (options.message) {
                    options.messages['requiredif'] = options.message;
                }
            }
        );
    
        jQuery.validator.addMethod('requiredif', function (value, element, params) {
            var otherValue = $(params).val();
            if (otherValue != null && otherValue != '') {
                return true;
            }
            return value != null && value != '';
        }, '');
    </script>
    
    @using (Html.BeginForm())
    {
        <div>
            @Html.LabelFor(x => x.Phone)
            @Html.EditorFor(x => x.Phone)
            @Html.ValidationMessageFor(x => x.Phone)
        </div>
    
        <div>
            @Html.LabelFor(x => x.Mobile)
            @Html.EditorFor(x => x.Mobile)
            @Html.ValidationMessageFor(x => x.Mobile)
        </div>
    
        <button type="submit">OK</button>
    }
    

    Pretty sick stuff for something so extremely easy as validation rule that we encounter in our everyday lives. I don't know what the designers of ASP.NET MVC have been thinking when they decided to pick a declarative approach for validation instead of imperative.

    Anyway, that's why I use FluentValidation.NET instead of data annotations to perform validations on my models. Implementing such simple validation scenarios is implemented in a way that it should be - simple.

    0 讨论(0)
  • 2020-11-28 08:14

    I know this question is not so hot, because it was asked relatively long time ago, nevertheless I'm going to share with a slightly different idea of solving such an issue. I decided to implement mechanism which provides conditional attributes to calculate validation results based on other properties values and relations between them, which are defined in logical expressions.

    Your problem can be defined and automatically solved by the usage of following annotations:

    [RequiredIf("Mobile == null",
        ErrorMessage = "At least email or phone should be provided.")]
    public string Phone{ get; set; }
    
    [RequiredIf("Phone == null",
        ErrorMessage = "At least email or phone should be provided.")]
    public string Mobile { get; set; }
    

    If you feel it would be useful for your purposes, more information about ExpressiveAnnotations library can be found here. Client side validation is also supported out of the box.

    0 讨论(0)
  • 2020-11-28 08:21

    Since nobody else suggested it, I'm going to tell you a different way to do this that we use.

    If you create a notmapped field of a custom data type (in my example, a pair of gps points), you can put the validator on that and you don't even need to use reflection to get all the values.

        [NotMapped]
        [DCGps]
        public GPS EntryPoint
        {
            get
            {
                return new GPS(EntryPointLat, EntryPointLon);
            }
        }
    

    and the class, a standard getter/setter

       public class GPS
    {
        public decimal? lat { get; set; }
        public decimal? lon { get; set; }
        public GPS(decimal? lat, decimal? lon)
        {
            this.lat = lat;
            this.lon = lon;
        }
    }
    

    and now the validator:

        public class DCGps : DCValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (!(value is GPS)) {
                return new ValidationResult("DCGps:  This annotation only works with fields with the data type GPS.");
            }
            //value stored in the field.
            //these come through as zero or emptry string.  Normalize to ""
            string lonValue = ((GPS)value).lonstring == "0" ? "" : ((GPS)value).lonstring;
            string latValue = ((GPS)value).latstring == "0" ? "" : ((GPS)value).latstring;
    
            //place validation code here.  You have access to both values.  
            //If you have a ton of values to validate, you can do them all at once this way.
        }
    }
    
    0 讨论(0)
提交回复
热议问题