4
votes

I'm using NinjectHttpApplication with couple modules defined in my project.

What I want is to create FluentValidation validation factory as described in http://www.thekip.nl/2011/09/22/using-fluentvalidation-for-both-domain-validation-and-validation-in-mvc-projects/.

To create a concrete validation factory I need to override

IValidator CreateInstance(Type validatorType) 

method where I should then call

return kernel.Get<validatorType>() as IValidator

But I've read that using IKernel outside of Global.asax scope is not recommended.

What options are there to make what I want?

EDIT: using Ninject-FluentValidation extension

As Remo's stated there's an extension on GitHub (https://github.com/ninject/ninject.web.mvc.fluentvalidation). There's a class in the extension:

public class NinjectValidatorFactory : ValidatorFactoryBase { ... }

which takes IKernel in constructor and creates instances of IValidator

public override IValidator CreateInstance(Type validatorType)
{
    if(((IList<IBinding>)Kernel.GetBindings(validatorType)).Count == 0)
    {
        return null;
    }

    return Kernel.Get(validatorType) as IValidator;
}

then my code goes like:

public class MvcApplication : NinjectHttpApplication
{
    private NinjectValidatorFactory nvfactory;

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());                        
    }
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Default",
            "{controller}/{action}/{id}",
            new { controller = "Employee", action = "Index", id = UrlParameter.Optional }
        );
    }        
    protected override void OnApplicationStarted()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);

        ModelValidatorProviders.Providers.Clear();
        ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(nvfactory));            
    }
    protected override IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Load(Assembly.GetExecutingAssembly());            
        nvfactory = new NinjectValidatorFactory(kernel);

        return kernel;
    }
}

That works. I don't know could it be resolved better though. Also I don't understand the need to expose IKernel as a public property on NinjectValidationFactory.

4

4 Answers

10
votes

The Ninject.Web.Mvc.FluentValidation extension adds support for fluent validation to Ninject. It can be found on NuGet. See https://github.com/ninject/ninject.web.mvc.fluentvalidation

4
votes

I highly advise reading Mark Seemann's Dependency Injection in .NET book.

To keep it simple, if you're looking to ask the container for a dependency, you're not using Dependency Injection. You don't call the container. It'll call you.

1
votes

This is pretty simple. I show you a simple console app to demonstrate FluentValidation with NInject.

  1. Create a console app in visual studio.
  2. Intsall the nuget packages FluentValidation and NInject.

Install-Package NInject

Install-Package FluentValidation

  1. Create the following classes.

    public class Customer
    {
        public int Id { get; set; }
        public string Surname { get; set; }
        public string Forename { get; set; }
        public decimal Discount { get; set; }
        public string Address { get; set; }
    }
    
    using FluentValidation;
    public class CustomerValidator : AbstractValidator<Customer>
    {
        public CustomerValidator()
        {
            //RuleFor(customer => customer.Surname).NotNull();
            RuleFor(customer => customer.Surname).NotNull().NotEqual("foo");
        }
    }
    
    using FluentValidation;
    using Ninject;
    using System;
    
    public class NInjectValidatorFactory : ValidatorFactoryBase
    {
        private readonly IKernel m_NInjectKernel;
        public NInjectValidatorFactory(IKernel kernel)
        {
            if (kernel == null)
                throw new ArgumentNullException("NInject kernel injected is null!!");
    
            m_NInjectKernel = kernel;
        }
        public override IValidator CreateInstance(Type validatorType)
        {
            return m_NInjectKernel.Get(validatorType) as IValidator;
        }
    }
    

    4.The main method inside the program class would be as follows.

    using FluentValidation;
    using Ninject;
    using System;
    using System.Linq;
    
    class Program
    {
        static void Main(string[] args)
        {
            // Set up the DI Container.
            var kernel = new StandardKernel();
            kernel.Bind<IValidator<Customer>>().To<CustomerValidator>().InSingletonScope();
    
            var nInjectvalidationFactory = kernel.Get<NInjectValidatorFactory>();
            var customer = kernel.Get<Customer>();
    
            var customerValidator = nInjectvalidationFactory.GetValidator<Customer>();
    
            var results = customerValidator.Validate(customer);
    
            if (!results.IsValid)
                results.Errors.ToList().ForEach(e =>
                {
                    Console.WriteLine(e.ErrorMessage);
                    Console.WriteLine(e.ErrorCode);
                    Console.WriteLine(e.PropertyName);
                    Console.WriteLine(e.ResourceName);
                    Console.WriteLine(e.Severity);
                }
                );
            Console.ReadLine();
        }
    }
    
    1. Just run this. The code is pretty self explanatory. Inside the main method, we are setting up the DI container, NInject in this case. Then we are doing the necessary bindings(mappings). Note only one mapping is required. That too as a singleton as explained here for performance.
0
votes

Depending on your implementation of the kernel, this is not a problem as such.

It is not recommended, as it creates a dependency on the kernel (and as such you are using Service Location and not Dependency Injection).

Another option would be to use Ninjects notion of Providers as described by Alexsander Beletsky.