If you are an ASP.NET MVC developer, FluentMvc is for you. with this library you can create validators for your model fluently without using any data annotations attributes.

This project based on .NET Framework 4.0
http://fasterflect.codeplex.com/ used in this project
This project can be installed via NuGet package manager: install-package FluentMVC

Usage of FluentMvc

To enable this framework on your project, write this code line in Application_Start() method:

FluentMvc.MvcIntegration.FluentValidatorProvider.Config();


For creating validatable model, you must Implement IModelInitializer<T> interface in your model. This interface will implement InitializeModel model method. in this method you can define you validaiton rules.

public class User : IModelInitializer<User>
{
    public string Username { get; set; }
    public string Password { get; set; }
    public string Email { get; set; }
    public string VerifyEmail { get; set; }

    public void InitializeModel(ModelContext<User> context)
    {
        context.RulesFor(user => user.Username)
            .Required(c => c.Message("Username is required").StopOnFail())
            .Length(3, 10, c => c.Message("User name length must between 3 and 10"));
        context.RulesFor(user => user.Password)
            .Required(c => c.Message("Password is required"));
        context.RulesFor(user => user.Username)
            .Email(c => c.Message("Please check email format!").StopOnFail())
            .EqualTo(user => user.VerifyEmail, c => c.Message("Email and VerifyEmail must be equals."));
    }
}

Built-In rules:

  1. Required
  2. Length: Specify min and max length for string proeprties
  3. Range: Speicfy min and max range for numeric properties
  4. RegEx: Regular Expressions Validators
  5. Email
  6. Url
  7. EqualsTo: Compare to property for equality

Conditions on validators

you can set conditions on validation.
  1. When: specify a condition to run validation rule
  2. Ignore: speicfy a condition to ignore validation rule

for example, email validation rule checked on Email property when Username is not null or empty:

public void InitializeModel(ModelContext<User> context)
{
    context.RulesFor(user => user.Email)
        .Email(c => c.Message("Email format is not currect").When(user => !string.IsNullOrEmpty(user.Username)));
}


other condition is Ignore, this conditions specify when rules must be ignored.

Custom Validators

Custom validators can be used in two diffrenet ways.

Predicate validators
with this validator type, you can use a method or lambda expression for validation. for example:

public void InitializeModel(ModelContext<User> context)
{
    context.RulesFor(user => user.Username)
        .CustomValidator(CheckUsername, c => c.Message("Username is not valid!"));
}

private bool CheckUsername(User user)
{
    if (user.Username != "Admin")
        return false;
    return true;
}


Validator Classes
you can create custom validator classes and use in many situations

public class RequiredEmailValidator : FluentValidator
{
    public override bool Validate(FluentValidationContext context)
    {
        if (context.Value == null)
            return false;
        if (context.Value is string && string.IsNullOrEmpty(context.Value.ToString()))
            return false;
        const string pattern = @"[\w\d!#$%&'*+-/=?^`{|}~]+(\.[\w\d!#$%&'*+-/=?^`{|}~]+)*@([a-z\d][-a-z\d]*[a-z\d]\.)*[a-z][-a-z\d]*[a-z]";
        if (!Regex.IsMatch(context.Value.ToString(), pattern))
            return false;
        return true;
    }
}


Now, you can use this validator class on your models:

public void InitializeModel(ModelContext<User> context)
{
    context.RulesFor(user => user.Email)
        .CustomValidator(new RequiredValidator(), c => c.Message("Email is required and must be email address."));
}

Unobtrusive client-side validation

This framework support built-in unobtrusive validation framework, for support client side validation, just implement IClientValidatable interface in your validator class:

public class RequiredEmailValidator : FluentValidator, IClientValidatable
{
    private const string pattern = @"[\w\d!#$%&'*+-/=?^`{|}~]+(\.[\w\d!#$%&'*+-/=?^`{|}~]+)*@([a-z\d][-a-z\d]*[a-z\d]\.)*[a-z][-a-z\d]*[a-z]";

    public override bool Validate(FluentValidationContext context)
    {
        if (context.Value == null)
            return false;
        if (context.Value is string && string.IsNullOrEmpty(context.Value.ToString()))
            return false;
        if (!Regex.IsMatch(context.Value.ToString(), pattern))
            return false;
        return true;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var requiredRule = new ModelClientValidationRule
                                {
                                    ValidationType = "required",
                                    ErrorMessage = ErrorMessage
                                };
        var emailRule = new ModelClientValidationRule
                            {
                                ValidationType = "regex",
                                ErrorMessage = ErrorMessage
                            };
        emailRule.ValidationParameters.Add("pattern", pattern);
        var validationRules = new List<ModelClientValidationRule> {requiredRule, emailRule};
        return validationRules;
    }
}


Built-In validation rules that support client-side validation:
  1. Required
  2. EqualsTo
  3. Length
  4. Range
  5. RegEx
  6. Email
  7. Url

Predicate based validation rules don't support client side validations.

Remote validation

Remote validations supported in FluentMvc framework, to use remote validations Unobtrusive client-side validation must be enabled.

for example, Validate username via Remote validation:

Controller action used for remote validation:

public class AccountController : Controller
{
    public JsonResult CheckUsername(string username)
    {
        if (username != "admin")
            return Json(true, JsonRequestBehavior.AllowGet);
        return Json("Username is not available!");
    } 
}


Model initializer for remote validation:

public void InitializeModel(ModelContext<User> context)
{
    context.RulesFor(user => user.Username)
        .Remote<AccountController>(controller => controller.CheckUsername);
}


Use additional fields for remote validation

With additional fields you can pass other parameters to your action:

public JsonResult CheckUsername(Models.User user)
{
    // Code for remote validation
}


and model initializer:

public void InitializeModel(ModelContext<User> context)
{
    context.RulesFor(user => user.Username)
        .Remote<AccountController>(controller => controller.CheckUsername, user => user.Email);
}


for using additional fields, action parameter must be of your model type to validate.

Localization

For localize rules messages, just use your localized message az input parameter:

public void InitializeModel(ModelContext<User> context)
{
    context.RulesFor(user => user.Username)
        .Required(c => c.Message(Resources.Messages.UsernameRequired));
}

Validation events

With validation events you can run some codes before or after validate property. Validation events can be used globally on all Models or on a property of model.

Global events
Global events can be used via FluentValidationProvider class. For example:

FluentValidatorProvider.BeforeValidate = context =>
                                            {
                                                // before validate
                                            };
FluentValidatorProvider.AfterValidate = args =>
                                        {
                                            // after valiate
                                        };


Object that passed to BeforeValidate is type of FluentValidationContext that contain these properties:
  1. Model: a reference to object that is validating
  2. PropertyName: name of property that is validating
  3. Value: value of property

Object that passed to AfterValidate is type of AfterValidateArgs that contain these propertpes:
  1. ValidationContext: that is type of FluentValidationContext
  2. IsValid: return true if property is validated
  3. ValidationResults: contains a list of validation error messages

Model Property Event Listener
For using this listener type, in InitializeModel method, use BeforeValidate and AfterValidate methods:

context.RulesFor(user => user.Username)
    .BeforeValidate(validationContext =>
                    {
                        // before validate property
                    })
    .AfterValidate(args =>
                    {
                        // after validate property
                    });


!Model Templates
One of the ASP.NET MVC features that is very usefull, is model templates. with model templates you can change behavior of rendering editors for model in views. this can be done in asp.net mvc with Attributes. but with FluentMVC you can change templates of your model fluently.

Built-In templates
  1. HiidenInput: specify that property must be rendered as hidden input. accept a boolean value that specify this property must be rendered as readonly or not.
  2. Display: set label text of property
  3. DataType: Provide information about meaning of the data
  4. UIHint: Set editor type of property, for example, MultilineText used for rendering editor as TextArea element
  5. AdditionalMetadata: additional parameters passed to model metadata
  6. DisplayFormat: change string format of property in display mode
  7. EditorFormat: change string fomat of property in editing mode
  8. NullText: display text of property when value is null
  9. ShowForEdit: specify that property is editable
  10. Order: order of property placement in editor
  11. Watermark: set watermark of property

Example of using model templates

public void InitializeModel(ModelContext<UserProfile> context)
{
    context.TemplatesFor(profile => profile.BirthDate)
        .DataType(DataType.Date)
        .Display("Your birthdate")
        .NullText("Not Entered");
    context.TemplatesFor(profile => profile.Address)
        .UIHint(UIHint.MultilineText)
        .NullText("Not Entered");
}

Last edited Nov 17, 2011 at 2:15 PM by devhandler, version 13