Tag Archives: mvc

MVC Validations 4: Whole class business object attribute validations

Sometimes you need to validate an object where the column data itself isn’t enough to determine if the object is valid from a business perspective. However, this doesn’t mean you have to abandon attributes for validation!

As an example say we have this class:

public class BusinessObject
{
    public int PropA { get; set; }
    public int PropB { get; set; }
    public int PropC { get; set; }
}

Our BusinessObject class here, say it’s only valid if the three properties have a sum of 100. Obviously we can’t set max and min values on the properties themselves to achieve this. What we need is an annotation attachable to the class itself. It works like this:

[AttributeUsage(AttributeTargets.Class)]
    public class NumberSumAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            var businessObject = value as BusinessObject;
            if (businessObject == null)
            {
                throw new ArgumentException("This annotation is not attachable to this object class");
            }
            return businessObject.PropA + businessObject.PropB + businessObject.PropC == 100;
        }
    }

Attach it to the head of your class like this

[NumberSumAttribute(ErrorMessage = "All fields must add up to 100.")]
public class BusinessObject
{
    public int PropA { get; set; }
    public int PropB { get; set; }
    public int PropC { get; set; }
}

That’s pretty much it. An obvious sticking point here is the class cast, but as far as I know I cannot restrict the validation attribute to only being applyable to some types. It’s possible to achieve this in some fashion by fidding around with access restrictions and inheritance like this if you really think you need this compile time security.

Oh, and since you won’t get the validation message on a field anymore you’ll need to stick the class generic validation messages somewhere. Add @Html.ValidationSummary(true) where you want these messages to appear, and you’re good to go!

Advertisements
Tagged , ,

MVC Validation 3: Unobtrusive validation in Ajax-loaded partial views

It’s a pretty good idea usually to not reload the entire page, but if you use MVCs now-standard unobtrusive validation you’ll find that the validation does not work on the partial you’ve just loaded.

Here’s how you solve this. Say you have a page which uses a script to load a view using ajax to a div, something like this:

function loadStuff()
{
    $('#myDiv').load(window.location.pathname + "/Stuff");
}

If you do this and don’t do anything else, the unobtrusive validation won’t work at all. If you try to brute force your way of of this by including the unobtrusive validation javascripts again on the partial view, the validation will kinda-sorta-work. But really, it doesn’t work. Instead what really want to do is to reparse and rebind the validation. Including this at the bottom of the partial view you want to load will work.

<script type="text/javascript">
    $(document).ready(function () {
        $.validator.unobtrusive.parse("#myDiv");
    });
</script>

I’m not sure if it would be a better fit to place this in the ajax-loader method instead. I think I had some problems with that and went with putting this in the bottom of the page. You can reparse the entire document if you want to no real ill effect, but parsing the div you just loaded is probably more efficient.

There’s nothing more to it really, but it’s a real gotcha that at had me confused at first.

Tagged , , ,

MVC Validation 2: Validating for column uniqueness

I find a common problem is handling the case of a column needing to be unique and how to handle this validation scenario. It’s easy enough to set a constraint in the backing SQL server, but handling this by catching an exception and then transferring this to the model state seems like a bad solution. What we want is to present a somewhat generic solution to this problem and end up with something like this:

class MyEntity {
    int Id { get; set; }
    
    [Unique(ErrorMessage="Name must be unique")]
    string Name { get; set; }
}

I want this working out of the box without having to do anything at all in the validation page. This presents a few problems. Mainly the issue of how to get the database context to the UniqueAttribute class that is the source of our [Unique] attribute, and how to construct these queries.

For the first problem there are a few choices, you can either set a ValidationContext manually and add your context as a dictionary parameter in the items field of the ValidationContext constructor. This solution is problematic as no of the built-in validation methods that gets run my MVC and Entity framework will provide that parameter. So that solution is out.

The second solution is to use the IServiceProvider interface, which is kinda sorta an inversion of control container. However, I found this to be a bit inflexible, and without using IServiceProvider to wrap a real dependency injection framework, which according to some is an anti-pattern, we’d end up with a new DbContextfor each instance.

So, using the solution presented earlier, this is what I came up with:

using System;
using System.ComponentModel.DataAnnotations;

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class UniqueAttribute : ValidationAttribute
{
	public String[] AdditionalMembers { get; set; }

	protected override ValidationResult IsValid(object value, ValidationContext context)
	{
		var uniqueValidator = IoC.Get<IUniqueValidator>();
		if (uniqueValidator == null)
		{
			throw new ValidationException(
				"No IUniqueValidator found in UniqueAttribute. Cannot determine uniqueness");
		}

		// MemberName may not be intialized when this is called. It will be later on
		if (context.MemberName != null)
		{
			if (!uniqueValidator.CanPropertyBeUniquelyStored(context.ObjectInstance, context.MemberName, AdditionalMembers))
			{
				return new ValidationResult(ErrorMessage, new[] {context.MemberName});
			}
		}

		return ValidationResult.Success;
	}
}

A few comments on this, it uses the IoC wrapper class described in a previous post. If you’re using a dependency injection framework out of the box, substitute this with whatever Kernel call you need to use. This in turn returns an interface that does the actual column uniqueness check. It would have been preferable to use constructor injection, but this won’t play nice with how .NET instantiates the attribute classes.

I’ve also included an AdditionalMembers to handle compound unique members. The member you attach the attribute to will get the validation message, the others will be validated along with the annotated member. Lets have a look at the interface.

using System;

public interface IUniqueValidator
{
	bool CanPropertyBeUniquelyStored(object entity, string propName, string[] additionalMembers);
}

No big deal, pass the entity, the property name and any additional members you want validated along with the member. Now for the implementation of this interface.

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Dynamic;

internal class UniqueValidator : IUniqueValidator
{
	private readonly IDbContext dbContext;

	public UniqueValidator(IDbContext dbContext)
	{
		this.dbContext = dbContext;
	}

	public bool CanPropertyBeUniquelyStored(object entity, string propName, string[] additionalMembers)
	{
		var entityType = entity.GetType();
		DbSet dbSet = dbContext.Set(entityType);

		var uniqueProperties = new List<string> {propName};
		if (additionalMembers != null)
		{
			uniqueProperties.AddRange(additionalMembers);
		}
		int propertyIndex = 0;
		string query = string.Join(" AND ", from a in uniqueProperties select GenerateQuery(a, entityType, propertyIndex++));
		object[] values = (from a in uniqueProperties select GetValue(a, entity, entityType)).ToArray();

		IQueryable list = dbSet.Where(query, values);

		var idProp = entityType.GetProperty("Id");
		var entityId = idProp.GetValue(entity, null);

		// Iterate through the list, if there is any entity which does not have the same ID 
		// as the one we are trying to insert, this property is not unique
		foreach (var listEntity in list)
		{
			var listEntityId = idProp.GetValue(listEntity, null);
			if (!listEntityId.Equals(entityId))
			{
				return false;
			}
		}

		return true;
	}

	private object GetValue(string propName, object entity, Type entityType)
	{
		var propertyInfo = entityType.GetProperty(propName);
		var propType = propertyInfo.PropertyType.Name;
		return propertyInfo.GetValue(entity, null);
	}

	private string GenerateQuery(string propName, Type entityType, int propertyIndex)
	{
		var propertyInfo = entityType.GetProperty(propName);
		var propType = propertyInfo.PropertyType.Name;            

		return propName + " == @" + propertyIndex;
	}
}

Again, we rely upon dependency injection to inject a IDbContext interface to the constructor. This is a simple interface derived from the Entity framework derived DbContext that provides us with at least the Set(Type entityType) method to get our hands on a DbSet. To construct the queries we use DynamicQuery, a smallish library that allows us to construct queries based on string constructs. This particular code relies on the primary key being called Id and being an Integer.

So, in all, the solution is easy enough if you have a working dependency injection framework. And if you don’t it should be easy enough to modify into using the MS IServiceProvider interface instead and creating a real DbContext instead of getting a derived interface injected. No reason to write custom validation outside of attributes either way.

Tagged , ,

MVC validation part 1: Custom unobtrusive validation using attributes

The validation process you get from MVC is pretty damn sweet. Just annotate your class, or entity for that matter, and you get instant validation in both your views and in your repository. It’s easy enough to use, just annotate your properties with whatever validation you want to use, and the validation appears. For reference, this is how it’s done:

 

public class MyValidateableObject 
{
    [Required(ErrorMessage = "You have to input something")]
    [StringLength(50, ErrorMessage = "It's too long!")]
    public string MyString { get; set; }
}

To get this to work with MVC, you just use Html.EditorFor(…) as you normally would. Also include a Html. ValidationMessageFor(model => model.MyString) to get the validation message to the client. To enable the client side unobtrusive validation in your app, add this to your Web.Config

<appSettings>
  <add key="ClientValidationEnabled" value="true" />
  <add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>

Also, do not forget to include the javascript files. You can typically do this in _Layout.cshtml, since your application probably will use these in most if not all of the views.

    <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>

With that out of the way, how do you validate something that isn’t already provided by the validators? And how to make this work client side? Here’s how!

First, you need to create a new attribute to use. Here we are going to create a validation that will accept any string with a length evenly dividable by a specific number. The annotation is pretty straightforward:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    public class StringLengthDivideableAttribute : ValidationAttribute
    {
        public int Divisor {get;set;}

        protected override ValidationResult IsValid(object v, ValidationContext validationContext)
        {
            if (v != null)
            {
                string value = (string)v;

                if (value.Length % Divisor != 0)
                {
                    return new ValidationResult(ErrorMessage, new [] { validationContext.MemberName });
                }
            }

            return ValidationResult.Success;
        }
    }

Note that the class name must end with Attribute. There are two methods to override, one which does not have a validationcontext available, and one who does. I suggest overriding the one with the validation context. It’s far more flexible than the other one. With this annotation in place you should be able to add this to your class

public class MyValidateableObject 
{
    [Required(ErrorMessage = "You have to input something")]
    [StringLength(50, ErrorMessage = "It's too long!")]
    [StringLengthDivideable(Divisor=2, ErrorMessage="Length not divisible by two")]
    public string MyString { get; set; }
}

Doing this will cause server side validation, and if you use it, entity framework validation validates too. But you won’t get the really nice unobtrusive validation. To get that, we need to provide three things: Flag the attribute as client validateable, provide a mapping for unobtrusive to use and make our own javascript function. Lets start with the attribute.

Change the attribute to also implement the IClientValidatable interface. This is found in System.Web.Mvc. This interface requires you to implement one method. Here’s what you need to do:

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
             var rule = new ModelClientValidationRule
                             {
                                 ErrorMessage = ErrorMessage,
                                 ValidationType = "stringdivisible"    
                             };
             rule.ValidationParameters.Add("divisor", Divisor);
             yield return rule;
         }

The ValidationType string is the important bit. That’s the key for the unobtrusive binding, which is the next step. Equally important is the ValidationParameters, which will become accessible to your javascript validator. Speaking of that, after you’ve included your jquery.validate.unobtrusive.js, add this:

jQuery.validator.unobtrusive.adapters.add('stringdivisible', ['divisor'], function (options) {
    var params = {
        divisor: options.params.divisor
    };

    options.rules['stringdivisible'] = params;
    options.messages['stringdivisible'] = options.message;
});

This code will match the divisor passed as a parameter in the ValidationParmeters to the params option and enable the rule used for the actual validation. If you don’t have any parameters to your function, pass true to options.rules[‘stringdivisible’].

You also need to add the actual method that will perform the validation

jQuery.validator.addMethod('stringdivisible', function (value, element, param) {
    if ((value.length % param.divisor) == 0)
        return true;
    return false;
}, '');

There you have it. That should be the total amount of work needed to make things run on the client side. Give it a try, hopefully most postback-only validation might be a thing of the past.

Tagged , ,
Advertisements