[RESOLVED]Model Validation Exceptions

Is there anyway I can use model data annotations as just a warning, without too much coding to override?  In other words the message won’t stop progress but clearly warns user of consequences?

you can use jquery alert for warnings instead

Model validation uses JQuery validation plugin. You can use the plugin directly to post the page along with errors. Please see the attached stack overflow post for two ways of doing this

jQuery validation plugin: how can I treat errors as warnings and allow form submit?

create

  • A custom data annotation attribute for all view models supporting the warning type of validation;
  • A known base class which the view models will inherit from;
  • We’ll have to duplicate the logic in JavaScript for each custom attribute.

Please note that the code below just illustrates the approach and I have to assume quite a lot of things without knowing the full context.

View Model

In this scenario it’s best to separate a view model from an actual model which is a good idea anyway. One possible approach is to have a base class for all view models which support warnings:

public abstract class BaseViewModel
{
    public bool IgnoreWarnings { get; set; }
}

The key reason a model needs to be separate is that there’s little sense in storing the 

IgnoreWarnings

property in your database.

Your derived view model will then look as follows:

public class YourViewModel : BaseViewModel
{
    [Required]
    [StringLengthWarning(MaximumLength = 5, ErrorMessage = "Your Warning Message")]
    public string YourProperty { get; set; }
}

StringLengthWarning

 is a custom data annotation attribute for server and client-side validation. It just supports the maximum length and can easily be extended with any other necessary properties.

Data Annotation Attribute

The core of the attribute is 

IsValid(value, validationContext

 method.

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class StringLengthWarningAttribute : ValidationAttribute, IClientValidatable 
{
    public int MaximumLength { get; set; }

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

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var model = validationContext.ObjectInstance as BaseViewModel;
        var str = value as string;
        if (!model.IgnoreWarnings && (string.IsNullOrWhiteSpace(str) || str.Length > MaximumLength))
            return new ValidationResult(ErrorMessage);
        return base.IsValid(value, validationContext);
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new StringLengthWarningValidationRule(MaximumLength, ErrorMessage);
    }
}

The attribute implements 

IClientValidatable

 and utilizes a custom client validation rule:

public class StringLengthWarningValidationRule : ModelClientValidationRule
{
    public StringLengthWarningValidationRule(int maximumLength, string errorMessage)
    {
        ErrorMessage = errorMessage;
        ValidationType = "stringlengthwarning";
        ValidationParameters.Add("maximumlength", maximumLength);
        ValidationParameters.Add("ignorewarningsfield", "IgnoreWarnings");
    }
}

lient-side JavaScript

Finally, to make it work, you’ll need the following JavaScript referenced from your view:

$(function () {
    $.validator.addMethod('stringlengthwarning', function (value, element, params) {
        var maximumlength = params['maximumlength'];
        var ignorewarningsfield = params['ignorewarningsfield'];

        var ctl = $("#" + ignorewarningsfield);
        if (ctl == null || ctl.is(':checked'))
            return true;
        return value.length <= maximumlength;
    });

    $.validator.unobtrusive.adapters.add("stringlengthwarning", ["maximumlength", "ignorewarningsfield"], function (options) {
        var value = {
            maximumlength: options.params.maximumlength,
            ignorewarningsfield: options.params.ignorewarningsfield
        };
        options.rules["stringlengthwarning"] = value;
        if (options.message) {
            options.messages["stringlengthwarning"] = options.message;
        }
    });

}(jQuery));

The JavaScript makes some assumptions you might want to revisit (the check-box name, etc).

UPDATE: HTML Helpers

To display the validation messages separately for errors and warnings, a couple of helpers will be necessary. The following class provides a sample:

public static class  MessageHelpers
{
    public static MvcHtmlString WarningMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        if (htmlHelper.ViewData.ModelState["IgnoreWarnings"] != null)
            return htmlHelper.ValidationMessageFor(expression);
        return MvcHtmlString.Empty;
    }

    public static MvcHtmlString ErrorMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        if (htmlHelper.ViewData.ModelState["IgnoreWarnings"] == null)
            return htmlHelper.ValidationMessageFor(expression);
        return MvcHtmlString.Empty;
    }
}

In the view they can be used as usual:

        @Html.EditorFor(model => model.YourProperty)
        @Html.ErrorMessageFor(model => model.YourProperty)
        @Html.WarningMessageFor(model => model.YourProperty)
 

I’m definitely into View Models and VM inheritance, however, that is a ton of code for a warning….   I see this solution as a long term framework enhancement.

I like the JQUERY validation idea as well. 

And finally, I’ll hop over to Stackoverflow too..

Thanks folks! :)

Leave a Reply