After looking at FluentValidation, I decided that most of what I needed was a way to do server-side validation of more complicated rules. While this is something FluentValidation does, it seemed like more than I needed so I set out to create a simple extension method to do what I needed. While it is still not necessarily perfect, it is simple and works.
1: public static void Validate<T, TResult>(this Controller controller, T model, Expression<Func<T, TResult>> expression, Func<TResult, bool> predicate, string message)
2: {
3: MemberExpression memberExpression = expression.Body as MemberExpression;
4:
5: if (memberExpression == null)
6: {
7: throw new ArgumentException("expression must be a property.");
8: }
9:
10: PropertyInfo propertyInfo = memberExpression.Member as PropertyInfo;
11:
12: if (propertyInfo == null)
13: {
14: throw new ArgumentException("expression must be a property.");
15: }
16:
17: TResult result = (TResult)propertyInfo.GetValue(model, new object[0]);
18:
19: if (!predicate(result))
20: {
21: string key = propertyInfo.Name;
22:
23: controller.ModelState.AddModelError(key, message);
24: }
25: }
Here is a sample implementation. Obviously this is a trivial case but any condition can be used and will add the error if the expression evaluates to false.
1: this.Validate(
2: model, // Model
3: m => m.Name, // Parameter to use for key
4: n => !string.IsNullOrEmpty(n), // Expression to add error if false
5: "Name cannot be blank."); // Message to add if expression returns false