Validation: How to inject A Model State wrapper with Ninject?
Asked Answered
K

2

33

I was looking at this tutorial http://asp-umb.neudesic.com/mvc/tutorials/validating-with-a-service-layer--cs on how to wrap my validation data around a wrapper.

I would like to use dependency inject though. I am using ninject 2.0

namespace MvcApplication1.Models
{
    public interface IValidationDictionary
    {
        void AddError(string key, string errorMessage);
        bool IsValid { get; }
    }
}

// wrapper

using System.Web.Mvc;

namespace MvcApplication1.Models
{
    public class ModelStateWrapper : IValidationDictionary
    {

        private ModelStateDictionary _modelState;

        public ModelStateWrapper(ModelStateDictionary modelState)
        {
            _modelState = modelState;
        }

        #region IValidationDictionary Members

        public void AddError(string key, string errorMessage)
        {
            _modelState.AddModelError(key, errorMessage);
        }

        public bool IsValid
        {
            get { return _modelState.IsValid; }
        }

        #endregion
    }
}

// controller

private IProductService _service;

public ProductController() 
{
    _service = new ProductService(new ModelStateWrapper(this.ModelState),
        new ProductRepository());
}

// service layer

private IValidationDictionary _validatonDictionary;
private IProductRepository _repository;

public ProductService(IValidationDictionary validationDictionary,
    IProductRepository repository)
{
    _validatonDictionary = validationDictionary;
    _repository = repository;
}

public ProductController(IProductService service)
{
    _service = service;
}
Kehoe answered 23/1, 2011 at 20:48 Comment(1)
Anyone who wants to do this with Autofac (mainly for asp.net core) see #45172519Lorileelorilyn
C
66

The solution given by that article mixes validation logic with the service logic. These are two concerns and they should be separated. When your application grows you will quickly find out that validation logic gets complicated and gets duplicated throughout the service layer. I, therefore, like to suggest a different approach.

First of all, it would IMO be much better to let the service layer throw an exception when a validation error occurred. This makes it more explicit and harder to forget to check for errors. This leaves the way the errors are handled to the presentation layer. The following listing shows a ProductController that uses this approach:

public class ProductController : Controller
{
    private readonly IProductService service;

    public ProductController(IProductService service) => this.service = service;

    public ActionResult Create(
        [Bind(Exclude = "Id")] Product productToCreate)
    {
        try
        {
            this.service.CreateProduct(productToCreate);
        }
        catch (ValidationException ex)
        {
            this.ModelState.AddModelErrors(ex);
            return View();
        }

        return RedirectToAction("Index");
    }
}

public static class MvcValidationExtension
{
    public static void AddModelErrors(
        this ModelStateDictionary state, ValidationException exception)
    {
        foreach (var error in exception.Errors)
        {
            state.AddModelError(error.Key, error.Message);
        }
    }
}

The ProductService class should not itself have any validation in it, but should delegate that to a class specialized to validation—i.e. the IValidationProvider:

public interface IValidationProvider
{
    void Validate(object entity);
    void ValidateAll(IEnumerable entities);
}

public class ProductService : IProductService
{
    private readonly IValidationProvider validationProvider;
    private readonly IProductRespository repository;

    public ProductService(
        IProductRespository repository,
        IValidationProvider validationProvider)
    {
        this.repository = repository;
        this.validationProvider = validationProvider;
    }

    // Does not return an error code anymore. Just throws an exception
    public void CreateProduct(Product productToCreate)
    {
        // Do validation here or perhaps even in the repository...
        this.validationProvider.Validate(productToCreate);

        // This call should also throw on failure.
        this.repository.CreateProduct(productToCreate);
    }
}

This IValidationProvider, however, should not validate itself, but should rather delegate the validation to validation classes that are specialized in validation one specific type. When an object (or set of objects) is not valid, the validation provider should throw a ValidationException, that can be caught higher up the call stack. The implementation of the provider could look like this:

sealed class ValidationProvider : IValidationProvider
{
    private readonly Func<Type, IValidator> validatorFactory;

    public ValidationProvider(Func<Type, IValidator> validatorFactory)
    {
        this.validatorFactory = validatorFactory;
    }

    public void Validate(object entity)
    {
        IValidator validator = this.validatorFactory(entity.GetType());
        var results = validator.Validate(entity).ToArray();        

        if (results.Length > 0)
            throw new ValidationException(results);
    }

    public void ValidateAll(IEnumerable entities)
    {
        var results = (
            from entity in entities.Cast<object>()
            let validator = this.validatorFactory(entity.GetType())
            from result in validator.Validate(entity)
            select result)
            .ToArray();

        if (results.Length > 0)
            throw new ValidationException(results);
    }
}

The ValidationProvider depends on IValidator instances, that do the actual validation. The provider itself doesn't know how to create those instances, but uses the injected Func<Type, IValidator> delegate for that. This method will have container specific code, for instance this for Ninject:

var provider = new ValidationProvider(type =>
{
    var valType = typeof(Validator<>).MakeGenericType(type);
    return (IValidator)kernel.Get(valType);
});

This snippet shows a Validator<T> class—I will show this class in a second. First, the ValidationProvider depends on the following classes:

public interface IValidator
{
    IEnumerable<ValidationResult> Validate(object entity);
}

public class ValidationResult
{
    public ValidationResult(string key, string message)
    {
        this.Key = key;
        this.Message = message; 
    }
    public string Key { get; }
    public string Message { get; }
}

public class ValidationException : Exception
{
    public ValidationException(ValidationResult[] r) : base(r[0].Message)
    {
        this.Errors = new ReadOnlyCollection<ValidationResult>(r);
    }

    public ReadOnlyCollection<ValidationResult> Errors { get; }            
}    

All the above code is the plumbing needed to get the validation in place. You can now define a validation class per entity you want to validate. However, to help your DI Container out a bit, you should define a generic base class for the validators. This will allow you to register the validation types:

public abstract class Validator<T> : IValidator
{
    IEnumerable<ValidationResult> IValidator.Validate(object entity)
    {
        if (entity == null) throw new ArgumentNullException("entity");

        return this.Validate((T)entity);
    }

    protected abstract IEnumerable<ValidationResult> Validate(T entity);
}

As you can see, this abstract class inherits from IValidator. Now you can define a ProductValidator class that derives from Validator<Product>:

public sealed class ProductValidator : Validator<Product>
{
    protected override IEnumerable<ValidationResult> Validate(
        Product entity)
    {
        if (entity.Name.Trim().Length == 0)
            yield return new ValidationResult(
                nameof(Product.Name), "Name is required.");

        if (entity.Description.Trim().Length == 0)
            yield return new ValidationResult(
                nameof(Product.Description), "Description is required.");

        if (entity.UnitsInStock < 0)
            yield return new ValidationResult(
                nameof(Product.UnitsInStock), 
                "Units in stock cnnot be less than zero.");
    }
}

As you can see the ProductValidator class uses the C# yield return statement which makes returning validation errors more fluent.

The last thing you should do to get this all working, is setting up the Ninject configuration:

kernel.Bind<IProductService>().To<ProductService>();
kernel.Bind<IProductRepository>().To<L2SProductRepository>();

Func<Type, IValidator> validatorFactory = type =>
{
    var valType = typeof(Validator<>).MakeGenericType(type);
    return (IValidator)kernel.Get(valType);
};

kernel.Bind<IValidationProvider>()
    .ToConstant(new ValidationProvider(validatorFactory));

kernel.Bind<Validator<Product>>().To<ProductValidator>();

Are we really done? It depends. Downside of the configuration above is that for each entity in our domain you will need a Validator<T> implementation. Even when perhaps most implementations will be empty.

You can solve this problem by doing two things:

  1. You can use Auto-Registration to automatically load all implementations dynamically from a given assembly.
  2. You can revert to a default implementation when no registration exists.

Such a default implementation could look like this:

sealed class NullValidator<T> : Validator<T>
{
    protected override IEnumerable<ValidationResult> Validate(T entity)
    {
        return Enumerable.Empty<ValidationResult>();
    }
}

You can configure this NullValidator<T> as follows:

kernel.Bind(typeof(Validator<>)).To(typeof(NullValidator<>));

After doing this, Ninject will return a NullValidator<Customer> when a Validator<Customer> is requested and no specific implementation is registered for it.

The last thing that's missing now is auto-registration. This will save you from having to add a registration per Validator<T> implementation and let Ninject search your assemblies dynamically for you. I couldn't find any examples of this, but I assume Ninject can do this.

UPDATE: See Kayess' answer to learn how to auto-register these types.

One last note: To get this done you need quite a lot of plumbing, so if your project is (and stays) fairly little, this approach might give you too much overhead. When your project grows, however, you will be very glad when you have such a flexible design. Think about what you have to do if you want to change the validation (to say Validation Application Block or DataAnnotations). The only thing you have to do is to write an implementation for the NullValidator<T> (I would rename it to DefaultValidator<T> in that case. Besides that, it is still possible to have your custom validation classes for extra validations that are hard to implement with other validation technologies.

Note that the use of abstractions such as IProductService and ICustomerService violates the SOLID principles and you might benefit from moving from this pattern to a pattern that abstracts use cases.

Update: Also take a look at this q/a; it discusses a follow-up question about the same article.

Consumerism answered 31/1, 2011 at 14:23 Comment(29)
@Consumerism - I ended up doing something similar as you have outlined but not as crazy. I guess the heart of the questions is what do you do when you have to bind something and pass it in. Like instance say I need to pass in the "email" address to my service layer since almost every method uses it. However I get the email from the cookie(Request.User.UserName) how would that work wtih ninject?Kehoe
@chobo2: Injecting the email address itself is often not a good thing to do, especially when you need it in many classes. If you think you need to use it in many classes, think hard about your design. Are you following the Single Responsibility Principle? I need to have more context to say something usefull about it. Please post a new question with detailed information about your problem here on SO and drop the link to it in the comment field below. This allows not only me, but also others to help. CheersConsumerism
@chobo2: "but not as crazy." hehehe... it is a bit abstract, isn't it? :-)Consumerism
@Steven: Thanks for linking to this thread, now you made me rethink my whole validation approach. Too much plumbing but I like the fact that it is extremely scalable! I have a question though, if the service class (say ProductService) is not responsible for the validation, then why do I need this class in the first place? If there isn't any validation logic inside it, then it's nothing more than calls to the repository, don't you think?Bahena
@Kassem, If your application only does CRUD operations, a service class can be overkill indeed. However, when you do more than inserting or updating a single record (such as inserting and updating multiple records of several entities, sending mails, communicating with another service, you name it) a service layer becomes extremely useful.Consumerism
@Steven: I see. I'm currently working on an online auction application. I am having some trouble deciding where some logic should go and what services I need. I think I'd better post a question about it here on StackOverflow and link to it.Bahena
@Steven: I have a few questions please... Could you explain what the Ninject code block is doing exactly? Also, if you have some experience with StructureMap could you convert that code to StructureMap syntax please? Moreover, do I still need to provide the a mapping of every IValidator<Type>, or does the factory figure it all out?Bahena
@Kassem: You see the line kernel.Bind<Validator<Product>>().To<ProductValidator>(); somewhere down my answer? You need to do this for each validator your create, or do batch registration, but again; I don't know how to do this in Ninject. For this question and the others you have: try asking here at SO, because I don't have all the answers. Good luck.Consumerism
Easily the craziest shit I have seen all day. I'm not ready for this type of IoC yet.Manumission
@Steven: Should be any of the validator implementations that rely on other services/repositories injected with those dependencies as well? How would you recommend propagating database access layer errors back to the controller? Thank you for the awesome post!Wittol
@see: I'm not sure I fully understand your question, but let me try anyway: If there is a error raised by the database (such as a FK constraint), the application should simply fail fast. Either there’s a bug (and you need to fix it) or your user experienced a rare race condition. In either case, don't translate those errors to user friendly validation error messages, because there is no way you can communicate in plain English what the problem is. Messages are often cryptic such as "you have become the deadlock victim" or "foreign key constraint FK_Users_Name failed". Does this help?Consumerism
@Steven: You should have your own SO tag for questions directed to you:) Good stuff!Dissentious
Hey Steven. Nice answer above.The only issue I'd have is that you throw an exception on a validation error. Don't you consider this overkill? I've always thought an exception should only be thrown in exceptional circumstances and a model failing validation isn't that for me. What are your thoughts?Hopeh
The Framework Design Guidelines say that an 'exceptional case' is more than only error cases. The FDG advices not to throw exceptions in the normal (happy) flow of the application, but that it’s okay to use it in the exceptional flow. A validation error is not the 'normal flow', but the 'exceptional flow', since the normal flow would be when the user entered all data correctly, so in that sense it's okay to throw. But more importantly, when any method does not do what it promises to do, it should throw an exception (FDG state this)...Consumerism
The service.CreateProduct method promises to create a product, but when it can’t do that (because of validation errors for instance) the only correct thing to do is to throw an exception. Besides, it would be cumbersome to return error codes and never mind letting that method fail silently. This doesn’t mean however that you can’t call some sort of validate method (that returns validation errors) before calling into the service, so there are more ways to skin a cat. However, IMO it is -at least- the service that should validate itself...Consumerism
All other validation (such as client-side validation) is optional (from an architectural perspective). When you call validate before calling the service, there is perhaps no need for catching the exception thrown from the service. Furthermore, if you mean by 'overkill' performance, the overhead of throwing a single exception during (for instance) a web request is negligible.Consumerism
@Consumerism +1 this is an excellent answer. I have one problem with it though that I don't know how to resolve. would you be able to take a look at this question and provide some advice?Boucher
@Consumerism - Thank you so much for the level of detail you went within this. It's helped me massively structuring an app I'm working on. Hope you don't mind but I have a quick question using the approach above. Where you see validation that requires access to the service or repository? Sticking with the product example above, how would your ProductValidator check that the Name of the product doesn't already exist in the db?Parfitt
@DaveHogan: I can't give a straight answer on this and these types of validation can get very complicated rather quickly. But the basic idea would be to query the database for that name, while leaving out the validated product in question.Consumerism
Thanks @Consumerism - I've discovered that! I'm simply trying to separate all concerns in a MVC app (presentation, service & repository) by using DI/IoC. The service being responsible for ensuring the validity of an entity (although not directly) before passing the entity to the repo. The ProductValidator in this instance requires a ProductService to make that Db query. I don't want to create a concrete instance of the product service in the Validator itself. Especially as creating a service depends on it. What's your thoughts on managing validation in the repository layer?Parfitt
I see no problems in having a validator that communicates with a database, but your ProductValidator should know nothing about your ProductService. Rather the other way around, but the name ProductService smells of a class with too many responsibilities. Instead you might be interested in a model where all operations are messages and you have a validator for each message.Consumerism
This is as much nuts as it is incredible. Thanks for sharing, in a way.Hotchpotch
Validation errors are exceptional. Just imagine, the user can literally send anything to your server and you really dont have the time to comprehend all the possibilities. So basically if something doesn't fit, "its not valid and is exceptional" is way better than "this isn't valid because..."Aegisthus
@Consumerism How do we do the ninject part if we are just using Asp.net core dependency injection without ninject? I'm stuck on the dynamic binding. IValidator ValidationProvider(Type type) { var valType = typeof(Validator<>).MakeGenericType(type); return (IValidator)kernel.Get(valType); } What's the equivalent of this without using ninject?Lorileelorilyn
@MartinDawson there is no support for batch registration with the OOTB implementation of .NET Core's DI Container. That container is really limited in features. You have to use a 3rd party extension on top of that library or use a 3rd party container like Autofac, Simple Injector or Ninject to be able to do this.Consumerism
@Consumerism Ok. Could you please post an example using Autofac? Ninject cannot be used with Asp.net core and I don't know enough about this to be able to patch it together. Thanks.Lorileelorilyn
@MartinDawson: Your statement is incorrect. Ninject can be used with ASP.NET Core. See this q/a and take a look at this example repository for an example of how to plug-in Ninject into ASP.NET Core. If you wish to use Autofac, here is an example of that.Consumerism
@Consumerism Couldn't get it to work. Don't know enough about Ninject to get it working as my setup is slightly different than example. It this possible with AutoFac? I just need to get the validatorFactory working.Lorileelorilyn
@MartinDawson: Sure this is possible with Autofac, but I can't help you with that.Consumerism
K
4

I would like to extend Stevens fantastic answer where he wrote:

The last thing that's missing now is automatic registration (or batch registration). This will save you from having to add a registration per Validator implementation and let Ninject search your assemblies dynamically for you. I couldn't find any examples of this, but I assume Ninject can do this.

He refers this code cannot be automagic:

kernel.Bind<Validator<Product>>().To<ProductValidator>();

Now imagine if you have tens of this like:

...
kernel.Bind<Validator<Product>>().To<ProductValidator>();
kernel.Bind<Validator<Acme>>().To<AcmeValidator>();
kernel.Bind<Validator<JohnDoe>>().To<JohnDoeValidator>();
...

So to overcome this a way I have found to make it automatic:

kernel.Bind(
    x => x.FromAssembliesMatching("Fully.Qualified.AssemblyName*")
    .SelectAllClasses()
    .InheritedFrom(typeof(Validator<>))
    .BindBase()
);

Where you can replace Fully.Qualified.AssemblyName with your actual assembly name fully qualified including your namespace.

UPDATE: to make this all work you need to install the NuGet package and use the Ninject.Extensions.Conventions namespace and use the Bind() method which accepts a delegate as parameter.

Kwon answered 27/10, 2016 at 12:6 Comment(3)
I couldn't make it work, it seems kernel.Bind not accepting delegate type as parameter?Haught
are you using any extension? Here is the IKernel implementation and there aren't any Bind method which takes delegate as parameter. Could you please check your module?Haught
Yes Ninject.Extensions.Conventions, you need to use that .Bind()Kwon

© 2022 - 2024 — McMap. All rights reserved.