Skip to content

Instantly share code, notes, and snippets.

@AlexBar
Created November 11, 2011 16:25
Show Gist options
  • Save AlexBar/1358426 to your computer and use it in GitHub Desktop.
Save AlexBar/1358426 to your computer and use it in GitHub Desktop.
CustomValidator MvcExtensions
using System;
using System.Collections.Generic;
using System.Web.Mvc;
namespace MvcExtensions
{
public class CustomValidationMetadata<TValidator> : ModelValidationMetadata where TValidator : CustomValidator, new()
{
public Action<TValidator> ConfigureValidator { get; set; }
protected override ModelValidator CreateValidatorCore(ExtendedModelMetadata modelMetadata, ControllerContext context)
{
var validator = new TValidator();
PopulateErrorMessage(validator);
if (ConfigureValidator != null)
{
ConfigureValidator(validator);
}
return new CustomValidatorDecorator(modelMetadata, context, validator);
}
public void PopulateErrorMessage(CustomValidator customValidator)
{
Invariant.IsNotNull(this, "customValidator");
string errorMessage = null;
if (ErrorMessage != null)
{
errorMessage = ErrorMessage();
}
if (!String.IsNullOrEmpty(errorMessage))
{
customValidator.ErrorMessage = ErrorMessage;
}
else if ((ErrorMessageResourceType != null) &&
(!String.IsNullOrEmpty(ErrorMessageResourceName)))
{
customValidator.ErrorMessageResourceType = ErrorMessageResourceType;
customValidator.ErrorMessageResourceName = ErrorMessageResourceName;
}
}
}
public abstract class CustomValidator
{
public abstract bool Validate(ValidatorContext context);
public Func<string> ErrorMessage { get; set; }
public string ErrorMessageResourceName { get; set; }
public Type ErrorMessageResourceType { get; set; }
}
public class ValidatorContext
{
public ValidatorContext(object model, ExtendedModelMetadata metadata)
{
Model = model;
Metadata = metadata;
}
public object Model { get; set; }
public ExtendedModelMetadata Metadata { get; set; }
}
public class CustomValidatorDecorator : ModelValidator
{
private readonly CustomValidator _customValidator;
public CustomValidatorDecorator(ExtendedModelMetadata metadata, ControllerContext controllerContext, CustomValidator customValidator)
: base(metadata, controllerContext)
{
_customValidator = customValidator;
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
var md = (ExtendedModelMetadata) Metadata;
var list = new List<ModelValidationResult>();
if (!_customValidator.Validate(new ValidatorContext(container, md)))
{
list.Add(new ModelValidationResult
{
//MemberName = md.PropertyName, --- do not need to set name here
Message = _customValidator.ErrorMessage()
});
}
return list;
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var clientValidatable = _customValidator as IClientValidatable;
if (clientValidatable == null)
{
yield break;
}
foreach (ModelClientValidationRule rule in clientValidatable.GetClientValidationRules(Metadata, ControllerContext))
{
yield return rule;
}
}
}
}
namespace MvcExtensions
{
using System;
using System.ComponentModel;
using System.Diagnostics;
/// <summary>
/// Defines a base class to fluently configure metadata.
/// </summary>
public class ModelMetadataItemBuilder<TValue> : IFluentSyntax
{
// ...
/// <summary>
/// Adds custom validator
/// </summary>
/// <param name="errorMessage">The error message.</param>
/// <returns></returns>
/// <typeparam name="TValidator"></typeparam>
/// <returns></returns>
public virtual ModelMetadataItemBuilder<TValue> AddCustomValidator<TValidator>(Func<string> errorMessage) where TValidator : CustomValidator, new()
{
return AddCustomValidator<TValidator>(null, errorMessage, null, null);
}
/// <summary>
/// Adds custom validator
/// </summary>
/// <param name="configure"></param>
/// <param name="errorMessage">The error message.</param>
/// <returns></returns>
/// <typeparam name="TValidator"></typeparam>
/// <returns></returns>
public virtual ModelMetadataItemBuilder<TValue> AddCustomValidator<TValidator>(Action<TValidator> configure, Func<string> errorMessage) where TValidator : CustomValidator, new()
{
return AddCustomValidator(configure, errorMessage, null, null);
}
/// <summary>
/// Adds custom validator
/// </summary>
/// <param name="configure"></param>
/// <param name="errorMessage">The error message.</param>
/// <param name="errorMessageResourceType">Type of the error message resource.</param>
/// <param name="errorMessageResourceName">Name of the error message resource.</param>
/// <returns></returns>
/// <typeparam name="TValidator"></typeparam>
/// <returns></returns>
protected virtual ModelMetadataItemBuilder<TValue> AddCustomValidator<TValidator>(Action<TValidator> configure, Func<string> errorMessage, Type errorMessageResourceType, string errorMessageResourceName) where TValidator : CustomValidator, new()
{
var customValidator = Item.GetValidationOrCreateNew<CustomValidationMetadata<TValidator>>();
customValidator.ConfigureValidator = configure;
customValidator.ErrorMessage = errorMessage;
customValidator.ErrorMessageResourceType = errorMessageResourceType;
customValidator.ErrorMessageResourceName = errorMessageResourceName;
return This;
}
// ...
}
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Demo.Web
{
using MvcExtensions;
public class ProductEditModel
{
public int Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
public Supplier Supplier { get; set; }
public decimal Price { get; set; }
}
public class ProductEditModelConfiguration : ModelMetadataConfiguration<ProductEditModel>
{
public ProductEditModelConfiguration()
{
Configure(model => model.Id).Hide();
Configure(model => model.Name).DisplayName(() => LocalizedTexts.Name)
.Required(() => LocalizedTexts.NameCannotBeBlank)
.AddCustomValidator<CustomValidatorTest>(() => "WTF?")
.MaximumLength(64, () => LocalizedTexts.NameCannotBeMoreThanSixtyFourCharacters);
Configure(model => model.Category).DisplayName(() => LocalizedTexts.Category)
.Required(() => LocalizedTexts.CategoryMustBeSelected)
.AsDropDownList("categories", () => LocalizedTexts.SelectCategory);
Configure(model => model.Supplier).DisplayName(() => LocalizedTexts.Supplier)
.Required(() => LocalizedTexts.SupplierMustBeSelected)
.AsListBox("suppliers");
Configure(model => model.Price).DisplayName(() => LocalizedTexts.Price)
.FormatAsCurrency()
.Required(() => LocalizedTexts.PriceCannotBeBlank)
.Range(10.00m, 1000.00m, () => LocalizedTexts.PriceMustBeBetweenTenToThousand);
}
public class CustomValidatorTest : CustomValidator
{
public override bool Validate(ValidatorContext context)
{
var m = (ProductEditModel) context.Model;
if (m.Name == "aaa")
return false;
return true;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment