Click here to Skip to main content
15,881,380 members
Articles / Programming Languages / C#

How To Easily Set Up Fluent Validation With Autofac

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
9 Mar 2017CPOL6 min read 22.4K   3   1
Configuring and using Fluent Validation and Autofac

This is part 2 of a series on configuring and using Fluent Validation and Autofac. Check out the other parts here:

Do you do client-side or server-side validation? Well, client-side validation gives users faster feedback. The trouble is, you can only do client-side validation for straightforward scenarios. At some point, you'll need to check the state of your application's data. If you use Data Annotations, that means reverting to server-side validation. It tends to be for more complex scenarios.

For example, you might have a bunch of validation logic that only applies to a specific type of user within the system. Or maybe you’re checking if a username exists. That means adding validation logic to your controller actions. Wouldn’t it be better to have it all in one place? With Fluent Validation, you can. For this article, we're just focusing on server-side validation. First, let’s have a look at the common approach, Data Annotations.

First Things First: A Bit Of Background

The default Model Binder that ships with MVC has validation hooks built in. This means you can hook a validator into the model binding process. It will run when a user posts a model back to the server from a form. That’s the most common scenario for validation. MVC has its own validators that you can use. All you do is decorate your models with attributes. These attributes come from the System.ComponentModel.DataAnnotations namespace. They include things like RequiredFieldValidator, RangeValidator, etc.

I don’t like this approach. Don’t get me wrong, it works for a variety of common scenarios. It has good support for server and client-side validation. I have a couple of issues with this, though. For starters, it adds a lot of noise to your models. You can end up with a lot of attributes polluting a model. I then find it quite hard to visualize the important stuff, the properties themselves. Testing becomes a pain, too. It’s difficult to test your validation logic in isolation. So what’s the alternative?

Fluent Validation is a small validation library, written by Jeremy Skinner. I’m a big fan, because it allows you to keep your validation logic separate. Instead of adding a bunch of attributes to your model, you write a bunch of rules inside a validator. These rules use the Fluent syntax, which makes them very readable.

Hang on a second, though. What’s this Fluent thing all about, anyway? Fluent is a great addition to the C# language. You write it by using extension methods. Here’s a quick, contrived example. Let’s say we have a Thing:

C#
public class Thing
{
    public string ThingName { get; set; }
    public int ThingId { get; set; }
}

We can add a Fluent interface to our Thing by adding a bunch of extension methods. Each method needs to accept the Thing as a parameter (plus anything else the method needs). It also needs to return the same Thing. This allows us to chain the methods together. Let’s take a quick look:

C#
public static class ThingExtensions
{
    public static Thing WithName(this Thing source, string name)
    {
        source.ThingName = name;
        return source;
    }

    public static Thing WithId(this Thing source, int id)
    {
        source.ThingId = id;
        return source;
    }
}

We can now chain our methods together:

C#
var thing = new Thing().WithName("dave").WithId(7);

Doing Things the Data Annotations Way

That’s one example of a Fluent interface. For another, we’ll be taking a look at the Fluent Validation library itself. We’re going to have a look at a ViewModel with a bunch of validation rules attached. We’ll start by adding Data Annotations to the ViewModel. We’ll then add another ViewModel and switch to Fluent Validation. Here’s our model:

C#
public class RegisterViewModel
{
    private const string EmailAddressMatchError = 
    "Your email addresses don't match. Please check and re-enter.";
    private const string PasswordMatchError = 
    "Your passwords don't match. Please check and re-enter.";

    [Required(ErrorMessage = "Please enter your username")]
    public string Username { get; set; }

    [Required(ErrorMessage = "Please enter your email address")]
    [Compare("EmailAgain", ErrorMessage = EmailAddressMatchError)]
    public string Email { get; set; }

    [Required(ErrorMessage = "Please re-enter your email address")]
    [Compare("Email", ErrorMessage = EmailAddressMatchError)]
    public string EmailAgain { get; set; }

    [Required(ErrorMessage = "Please enter your password")]
    [Compare("PasswordAgain", ErrorMessage = PasswordMatchError)]
    [StringLength(20, ErrorMessage = "Your password must be 
    at least {2} characters long.", MinimumLength = 6)]
    public string Password { get; set; }

    [Required(ErrorMessage = "Please re-enter your password")]
    [Compare("Password", ErrorMessage = PasswordMatchError)]
    public string PasswordAgain { get; set; }
}

As you can see, it’s got a bunch of attributes against each property. When a user submits the form, the validation rules get triggered. If any of them fail, the Controller’s ModelStateDictionary fills up with errors. In the Controller Post action, we look at ModelState to see if there are any validation errors. If there are, we re-display the form and show the errors. If validation passes, we may do some database stuff and redirect elsewhere. It depends on the application. Here’s what our example Controller looks like:

C#
[HttpPost]
public ActionResult Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        // do something here with the model, like saving the user details in the data store

        return RedirectToAction("SignIn");
    }

    return View(model);
}

View the original article.

The view itself contains HtmlHelper methods for the validation summary and individual messages. Here’s what ours looks like:

HTML
@model Levelnis.Learning.AutofacExamples.Web.CommandQuery.ViewModels.RegisterViewModel

@{
    ViewBag.Title = "Levelnis Learning Series: Register Validation Example";
}

@section headstyles{
    <style>
        .field-validation-error {
            color: #a94442;
        }
    </style>
}

@using (Html.BeginForm("Register", "Security", 
FormMethod.Post, new { name = "registerForm", role = "form" }))
{
    <div class="row">
        <div class="col-xs-12 col-sm-8">
            <h2>Register</h2>
            <p>Here's a quick example showing how to hook up 
            validation using DataAnnotations. Feel free to browse around 
            and use the code as you see fit :>(/p>
            @Html.ValidationSummary(true, "There were some issues 
            with the data you entered. Please review the issues below 
            and try again:", new { @class = "text-danger" })
        </div>
    </div>
    <div class="row">
        <div class="col-xs-6 col-sm-4">
            <div class="form-group">
                @Html.LabelFor(m => m.Username, 
                new { @class = "control-label" })
                @Html.TextBoxFor(m => m.Username, 
                new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.Username)
            </div>
            <div class="form-group">
                @Html.LabelFor(m => m.Email, 
                new { @class = "control-label" })
                @Html.TextBoxFor(m => m.Email, 
                new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.Email)
            </div>
            <div class="form-group">
                @Html.LabelFor(m => m.EmailAgain, 
                "Email (again)", new { @class = "control-label" })
                @Html.TextBoxFor(m => m.EmailAgain, 
                new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.EmailAgain)
            </div>
        </div>
        <div class="col-xs-6 col-sm-4">
            <div class="form-group">
                @Html.LabelFor(m => m.Password, 
                new { @class = "control-label" })
                @Html.PasswordFor(m => m.Password, 
                new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.Password)
            </div>
            <div class="form-group">
                @Html.LabelFor(m => m.PasswordAgain, 
                "Password (again)", new { @class = "control-label" })
                @Html.PasswordFor(m => m.PasswordAgain, 
                new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.PasswordAgain)
            </div>
        </div>
    </div>
    <div class="row">
        <div class="col-xs-12 col-sm-8">
            <button type="submit" 
            name="btnRegister" id="btnRegister" 
            class="btn btn-primary pull-right">Register</button>
        </div>
    </div>
}

@section scripts{
    <script>
        (function ($) {
            $('.field-validation-error').each(function () {
                $(this).parent().addClass('has-error');
            });
        })(jQuery);
    </script>
}

The main things to note here are the style and script enhancements. These highlight the error messages more clearly. I’ve added an optional headstyles section to the layout page as well.

How Does Fluent Validation Fit In?

So that’s validation with Data Annotations, in a nutshell. What happens if we use Fluent Validation instead? Well, for starters, we can get rid of all those attributes. Here’s a new ViewModel that’s trimmed down a little:

C#
public class RegisterFluentViewModel
{
    public string Username { get; set; }
    public string Email { get; set; }
    public string EmailAgain { get; set; }
    public string Password { get; set; }
    public string PasswordAgain { get; set; }
}

No attributes anywhere here. Let’s add the Fluent Validation MVC package and link it all up. Enter this in the package manager console:

C#
Install-Package FluentValidation.MVC5

Now we’re ready. Let’s go ahead and move that logic into its own, self-contained validator. All we do is create a class that inherits from AbstractValidator. We pass the type that we’re validating in as a generic argument. In our case, it’s RegisterFluentViewModel:

C#
public class RegisterFluentViewModelValidator : 
AbstractValidator<RegisterFluentViewModel>
{
    public RegisterFluentViewModelValidator()
    {
        RuleFor(m => m.Username)
            .NotNull().WithMessage("Please enter your username");
        RuleFor(m => m.Email)
            .NotNull().WithMessage("Please enter your email address")
            .EmailAddress().WithMessage("Please enter a valid email address")
            .NotEqual(m => m.EmailAgain).WithMessage
            ("Your email addresses don't match. Please check and re-enter.");
        RuleFor(m => m.EmailAgain)
            .NotNull().WithMessage("Please enter your email address again")
            .EmailAddress().WithMessage("Please enter a valid email address again")
            .NotEqual(m => m.Email).WithMessage
            ("Your email addresses don't match. Please check and re-enter.");
        RuleFor(m => m.Password)
            .NotNull().WithMessage("Please enter your password")
            .NotEqual(m => m.PasswordAgain).WithMessage
            ("Your passwords don't match. Please check and re-enter.")
            .Length(6, 20).WithMessage("Your password must be at least 6 characters long.");
        RuleFor(m => m.PasswordAgain)
            .NotNull().WithMessage("Please enter your password again")
            .NotEqual(m => m.Password).WithMessage
            ("Your passwords don't match. Please check and re-enter.");
    }
}

All our rules are self-contained. If we need to add any more, we follow the same pattern. We can go far deeper than these straightforward rules as well. In the next article, we’ll take a look at using Must and Custom to perform any validation that’s required.

We’ve also got a class that we can unit test. The final article in this series will explore the approach I use for doing that.

Shouldn't Autofac Make An Appearance About Now?

As things stand, we’ll need to create a RegisterFluentViewModelValidator in our Controller. That’s not that helpful, though. We want the validator to be invisible to our Controller. When we post the RegisterFluentViewModel back, we want validation to occur automatically. To do this, we need Autofac to be set up in our project. Take a look at the previous article to understand more: How To Quickly Set Up Dependency Injection With Autofac

Still with me? Great. Next, we need to provide our own Validator Factory. This allows Autofac to find the right validator for the type we’re validating. Once we have a factory, we configure the FluentValidationModelValidatorProvider and pass the factory in. That provider is part of Fluent Validation, and is where the magic happens. It’s all open source, so feel free to poke around inside if you want to understand it better. Here’s our factory:

C#
public class AutofacValidatorFactory : IValidatorFactory
{
    private readonly IComponentContext container;

    public AutofacValidatorFactory(IComponentContext container)
    {
        this.container = container;
    }

    public IValidator<T> GetValidator<T>()
    {
        return (IValidator<>)GetValidator(typeof(T));
    }

    public IValidator GetValidator(Type type)
    {
        var genericType = typeof(IValidator<>).MakeGenericType(type);
        object validator;
        if (container.TryResolve(genericType, out validator))
            return (IValidator)validator;

        return null;
    }
}

Now we have our factory, we need to tell Fluent Validation about it. Head over to your startup code. Find the place where you set the MVC Dependency Resolver:

C#
var mvcResolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(mvcResolver);

Add this just below:

C#
FluentValidationModelValidatorProvider.Configure(provider =>
{
    provider.ValidatorFactory = new AutofacValidatorFactory(container);
});

That sets up the Validator Provider. It also adds the provider to the MVC ModelValidatorProviders collection, along with our factory. The final step is to register the IValidator dependencies with Autofac. These work in the same way as the IViewModelFactory dependencies we set up in the previous article. We’ll add them to the WebModule, which now looks like this:

C#
public class WebModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterAssemblyTypes(typeof(WebModule).Assembly)
            .Where(t => t.IsClosedTypeOf(typeof(IViewModelFactory<>)) || 
            t.IsClosedTypeOf(typeof(IViewModelFactory<,>)))
            .AsImplementedInterfaces().InstancePerLifetimeScope();

        builder.RegisterAssemblyTypes(typeof(WebModule).Assembly)
            .Where(t => t.IsClosedTypeOf(typeof(IValidator<>)))
            .AsImplementedInterfaces().InstancePerLifetimeScope();

        builder.RegisterType<HttpContextAccessor>().As
        <IHttpContextAccessor>().InstancePerLifetimeScope();
        builder.RegisterType<UserHandler>().As
        <IUserHandler>().InstancePerLifetimeScope();

        builder.RegisterFilterProvider();
        builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
        builder.RegisterControllers(Assembly.GetExecutingAssembly()).InjectActionInvoker();

        builder.RegisterModule<CoreModule>();
    }
}

Now we’re ready to go. Go ahead and create a RegisterFluent view and 2 RegisterFluent actions in the Security Controller. They should be identical to the Register ones except for a different ViewModel. The code download that accompanies this article should help you if you're getting stuck here. Here are our new Controller actions:

C#
[HttpPost]
public ActionResult RegisterFluent(RegisterFluentViewModel model)
{
    if (ModelState.IsValid)
    {
        // do something here with the model, 
        // like saving the user details in the data store

        return RedirectToAction("SignIn");
    }

    return View(model);
}

As you can see, they're the same as the Register ones, except for the different ViewModel. This was just to allow me to show the two approaches side-by-side.

View the original article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead Levelnis Ltd
United Kingdom United Kingdom
Follow along my journey as I create a newsletter about launching websites. Just message me with "I'm in" and I'll add you

Comments and Discussions

 
QuestionWebApi? Pin
Daniel Wagner25-Sep-17 0:25
Daniel Wagner25-Sep-17 0:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.