WCF Validation

The great thing about writing code is that you are almost assured that whatever you think would “be cool” someone out there in the world has written already. For example, I wanted to use DataAnnotations on my WCF model for validation. Silly me, I thought it would just work. I mean, it makes sense, right? Well, while it doesn’t work out of the box, it is something that makes sense, so someone wrote the code already!

I found this post over at DevTrends. It explains the logic behind using DataAnnotations for validation. My favorite part:

So a fantastic validation framework is available with .NET. All we need to do is integrate it into WCF.

Really, I am surprised this isn’t part of the framework, but whatever. Paul created a NuGet package, and that makes it so easy that it may as well be part of the framework. Thanks, Paul!
🙂

FluentValidation AddToModelState

HOORAY FOR THIS!

I was working on some admin functionality, and I added a validator to a model. The problem is the forms are contained within Telerik MVC Extensions tabs, so the validators for one tab are applied even if you are on a different tab. No dice! I really wanted to offer some nice error messages though, so I refused to quit! 🙂

I thought, “why can’t I just new up my validator manually”. I did this, and at first I wasn’t seeing what I needed. I eventually found that I had to get the result of validator.Validate(model) and then check result.IsValid. I was originally assuming I’d just be checking validator.IsValid. Anyway, here is what I had to that point:

var validator = 
    new Nop.Admin.Validators.Catalog
        .AddProductRelationshipAttributeValidator(_localizationService);
var result = validator.Validate(model);
if (result.IsValid == false) {
    // Do some error stuff here
}

The only thing now was how to get the result errors into the model state? I found a great thread on the FluentValidation discussion board that was asking the same thing – do I have to iterate the errors and manually add them to the ModelState? It turns out the answer is “yes”, but there is the handy extension method mentioned in the subject! TADA! So now, I have this nice bit of code:

var validator = 
    new Nop.Admin.Validators.Catalog
        .AddProductRelationshipAttributeValidator(_localizationService);
var result = validator.Validate(model);
if (result.IsValid == false) {
    Response.StatusCode = 400;
    result.AddToModelState(ModelState, "");
    return ModelState.JsonValidation(JsonRequestBehavior.AllowGet);
}

The ModelState.JsonValidation bit is another extension method I have to put the ModelState into a standardized JSON result that I then pass through a couple of js functions. Those functions make sure the validation passes, and if the validation does not pass it puts the errors in the validation summary manually. It is all pretty handy stuff.

If anyone has any better ideas, I am all ears!

Recent gists

I have saved a couple of code snippets lately and just wanted to add them to the blog too. This one enables “focus out” unobtrusive validation. I use this in a couple of forms on the site. Part of this gist also has a little code where I was messing around with “remote” validation to get it to work with the focus out validation – of course, I wasn’t thinking, just geeking out. The remote stuff is already baked in to jquery validation and I already had code to handle the focus out portion, so it wasn’t really worth the time, but I didn’t spend much on it. Anyway, the code:

window.Em = window.Em || {};

Em = {
    setFocusOutValidation: function (form) {
        var s = $.data(form, "validator").settings;
        s.onfocusout = function (element) {
            if ($(element).val().length > 0) {
                $(element).valid();
            }
        };
        s.showErrors = function (map, list) {
            this.defaultShowErrors();
            if (list && list.length > 0) {
                for (prop in map) {
                    $("#" + prop).focus();
                }
            }
            Em.displayValidationSummaryErrors(list);
        };
        return s;
    },
    displayValidationSummaryErrors: function (list) {
        $("[data-valmsg-summary]")
            .removeClass("validation-summary-valid")
            .addClass("validation-summary-errors");
        $("[data-valmsg-summary] ul").html("");
        $.each(list, function (idx, data) {
            $("[data-valmsg-summary] ul").append($("<li></li>")
                .html(data.message));
        });
    }
}

The focus out validation will check if the field is valid (also remotely) and if not, the focus will stay on the invalid field. Sometimes it can get a little wonky, but for the most part it works well. Typically when it doesn’t seem to “work” it is because the validation is setup backwards, like comparing a “new password” to the “confirm password”, rather than the other way around.

Gist

The next gist is a Telerik MVC grid extension I started messing with to handle filtering. Turns out I was making it harder than necessary, and there was already an extension method in the NopCommerce codebase. Oh well, here it is anyway:

public static IList<T> ApplyFilter<T>(this IList<T> list, 
                                      FilterDescriptor filter) {
 
    // We wont allow filtering on anything but string type properties
    if (filter.MemberType != typeof(string)) 
        throw new ArgumentException(@"Filtering is only allowed 
            for properties with a type of 'string'.");
 
    var value = filter.Value.ToString();
    switch (filter.Operator) {
        case FilterOperator.IsEqualTo:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member)
                    .GetValue(x, null);
                return String.Equals((string)propertyValue, value, 
                    StringComparison.InvariantCultureIgnoreCase);
            }).ToList();
            break;
 
        case FilterOperator.IsNotEqualTo:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return String.Equals((string)propertyValue, value, 
                    StringComparison.InvariantCultureIgnoreCase) == false;
            }).ToList();
            break;
 
        case FilterOperator.StartsWith:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return ((string)propertyValue).StartsWith(value, 
                    StringComparison.InvariantCultureIgnoreCase);
            }).ToList();
            break;
 
        case FilterOperator.Contains:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return ((string)propertyValue).IndexOf(value, 
                    StringComparison.InvariantCultureIgnoreCase) > -1;
            }).ToList();
            break;
 
        case FilterOperator.EndsWith:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return ((string)propertyValue).EndsWith(value, 
                    StringComparison.InvariantCultureIgnoreCase);
            }).ToList();
            break;
    }
 
    return list;
}

Gist

The last one is just a little quickie example of how to select data attributes with jQuery:

var d = $("input[data-toggle-class]");
$.each(d, function (idx, data) {
    console.log(idx, data);
});

Gist

I was originally looking for a way to select a wildcard in the data attribute, but I couldn’t get it working. Anyway, this works fine – though I did not actually end up using it. 🙂

Conditional validation

Related to my recent post about cascading drop down lists, I needed to do some conditional validation based on a drop down list selection. I found a great post on Simon Ince’s blog about conditional validation in MVC 3. He even has a new project to wrap up some of the conditional functionality that he blogged about, but I decided to roll my own since I would learn more going that route.

Using the project attached to his post, I started dissecting what he was doing. After a while, I got the client validation working, but I realized I was so focused on the client/server validation piece that I did not get my conditions correct! After a few tweaks, I finally got everything working. Here is the custom attribute:

public class ConditionalMaximumWeightAttribute : ValidationAttribute, 
                                                 IClientValidatable {

    private const string ERRORMSG = "Weight must not exceed {0} lbs.";

    public string DependentProperty { get; set; }
    public string DependentValue { get; set; }
    public int MaximumWeight { get; set; }

    public ConditionalMaximumWeightAttribute(string dependentProperty, 
                                             string dependentValue, 
                                             int maximumWeight) {
        this.DependentProperty = dependentProperty;
        this.DependentValue = dependentValue;
        this.MaximumWeight = maximumWeight;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
                         ModelMetadata metadata, ControllerContext context) {
        var rule = new ModelClientValidationRule() {
            ErrorMessage = String.Format(ERRORMSG, this.MaximumWeight),
            ValidationType = "maximumweight",
        };

        string depProp = BuildDependentPropertyId(metadata, context 
                                                            as ViewContext);

        rule.ValidationParameters.Add("dependentproperty", depProp);
        rule.ValidationParameters.Add("dependentvalue", this.DependentValue);
        rule.ValidationParameters.Add("weightvalue", this.MaximumWeight);

        yield return rule;
    }

    protected override ValidationResult IsValid(object value, 
                                        ValidationContext validationContext) {
        // get a reference to the property this validation depends upon
        var containerType = validationContext.ObjectInstance.GetType();
        var field = containerType.GetProperty(this.DependentProperty);

        if (field != null) {
            // get the value of the dependent property
            var dependentvalue = 
                field.GetValue(validationContext.ObjectInstance, null);

            var weight = 
                containerType.GetProperty(validationContext.DisplayName);
            int weightvalue = 
                (int)weight.GetValue(validationContext.ObjectInstance, null);

            // compare the value against the target value
            if (dependentvalue == this.DependentValue && 
                                  weightvalue > this.MaximumWeight) {
                // validation failed - return an error
                return new ValidationResult(String.Format(ERRORMSG, 
                                            this.MaximumWeight));
            }
        }

        return ValidationResult.Success;
    }

    private string BuildDependentPropertyId(ModelMetadata metadata, 
                                            ViewContext viewContext) {
        // build the ID of the property
        string depProp = viewContext.ViewData.TemplateInfo
                         .GetFullHtmlFieldId(this.DependentProperty);
        // unfortunately this will have the name of the current field appended 
        // to the beginning,
        // because the TemplateInfo's context has had this fieldname appended 
        // to it. Instead, we
        // want to get the context as though it was one level higher (i.e. 
        // outside the current property,
        // which is the containing object (our Person), and hence the same 
        // level as the dependent property.
        var thisField = metadata.PropertyName + "_";
        if (depProp.StartsWith(thisField))
            // strip it off again
            depProp = depProp.Substring(thisField.Length);
        return depProp;
    }
}

Gist

To put this in context, I need to restrict a maximum weight allowed for a carrier. At a certain point, we may as well go with another carrier because it is cheaper. However, I don’t want to restrict the maximum weight if the carrier that has been selected is the cheaper carrier.

The attribute is used like so, where PropertyName is the name of the related property to check, PropertyValue is the conditional value of the property, and MaximumWeight is an integer representing the maximum weight for the conditional:

public class ...

    [ConditionalMaximumWeight("PropertyName", "PropertyValue", MaximumWeight)]
    [Required(ErrorMessage = "Weight required")]
    public int? Weight { get; set; }

    ...
}

Here is the client-side implementation:

<script type="text/javascript">
    $.validator.addMethod("maximumweight",
        function (value, element, parameters) {
            var carrier = $("#" + parameters["dependentproperty"]).val();
            var carriervalue = parameters["dependentvalue"].toString();
            var weightvalue = Number(parameters["weightvalue"]);
            if (carrier == carriervalue && value > weightvalue) {
                return false;
            }
            return true;
        }
    );

    $.validator.unobtrusive.adapters.add(
    "maximumweight",
    ["weightvalue", "dependentproperty", "dependentvalue"],
    function (options) {
        options.rules["maximumweight"] = {
            weightvalue: options.params["weightvalue"],
            dependentproperty: options.params["dependentproperty"],
            dependentvalue: options.params["dependentvalue"]
        };
        options.messages["maximumweight"] = options.message;
    });
</script>

Gist

I should probably do a little null checking in on the client-side, but I may just find that out the hard way! 🙂