Validation: How to Inject a Model State Wrapper with Ninject

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

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.

How to inject ModelState as parameter with Ninject?

ModelState is part of the application's runtime state. Therefore, it is not available at the point in time when you compose the application with Ninject.

You could work around this limitation by creating an Abstract Factory to create your AccountService and also make it a part of your application's runtime state.

public interface IAccountServiceFactory
{
IAccountService Create(IValidationDictionary validationDictionary);
}

public class AccountServiceFactory
{
public IAccountService Create(IValidationDictionary validationDictionary)
{
return new AccountService(validationDictionary);
}
}

And then in your AccountController, inject an AccountServiceFactory instead of an AccountService.

   private readonly IAccountServiceFactory serviceFactory; 

public AccountController(ILanguageService ls, ISessionHelper sh, IAccountServiceFactory asf)
{
this.serviceFactory = asf;
this.languageService = ls;
this.sessionHelper = sh;

}

public void DoSomething()
{
var accountService = this.serviceFactory.Create(new ModelStateWrapper(this.ModelState));

// Do something with account service
}

Alternatively, you could pass the runtime dependency directly to the account service through each public method call where it is required.

this.service.DoSomething(new ModelStateWrapper(this.ModelState));

Ninject - Inject ModelState into Service?

It seems like you would have a problem trying inject ModelState because of the fact that once in an action method you already have the instance of ModelState that you want to pass into your wrapper.

Not every single object needs to or should be injected. Sometimes, this case potentially being one of those times, it is cleaner and easier and simpler to "inject" or provide the dependency or object as a parameter to the method that requires it. That is, pass IValidationDictionary as a parameter to the methods in your Service. Construct a ModelStateWrapper in the controller (new it up or use a Factory), and pass it in.

Dependency Injection: No Service for type

var scopeFactory = services
.BuildServiceProvider()
.GetRequiredService<IServiceScopeFactory>();

With this, you are building a separate service provider and use that service provider’s services to get a service scope factory.

So when you later do the following:

return (IValidator)scopeFactory.CreateScope().ServiceProvider.GetRequiredService(valType);

You are using that separate service provider. And when you resolve services from that service provider, then these are completely separate from the ones your application is running inside.

And since you have registered the Validator<Promotion> only after you have created that separate service provider, that service provider will not include your validator service.

It is generally not a good idea to have multiple service providers within the same application. It will just cause services to have multiple lifetimes, e.g. singletons exist once per service provider, and you will get problems when you interact with services from other providers (like you have now). Instead, you should try to solve your problem using only a single service provider.

In your case, you could get there for example by changing the registration of your validation provider:

services.AddSingleton<IValidationProvider>(sp =>
{
Func<Type, IValidator> validatorFactory = type =>
{
var valType = typeof(Validator<>).MakeGenericType(type);
return sp.GetRequiredService(valType);
};

return new ValidationProvider(validatorFactory);
});

The sp that gets passed to the factory there is the service provider, the one correct one. So you can use that directly to resolve services you need to initialize the provider.

You will notice that I do not create a new service scope within the validatorFactory. This is because when you use service scopes, you should actually always dispose them after use. If you just create them and resolve a service from them, then the service scope will be kept open and you may run into issues later.

In your case, you cannot dispose of the service scope properly, because you need to return the resolved object and want to use it afterwards. So that means that using a service scope is not a good idea here. You should also consider whether you really need a service scope in general: ASP.NET Core already creates a service scope for every request, so it would make sense to just reuse that scope if you really need a scoped dependency.

Unfortunately, this also means that your implementation of ValidationProvider is not really compatible to this. You could make ValidationProvider itself a scoped service, so that it can safely access scoped services from the service provider (without having to manage its own service scope) or if you really need it to be a singleton, you could also move this logic into the validation provider implementation itself.

Service Layer Validation

I think quite an elegant way would be to have a validate method on your service that returns a dictionary of model errors from business logic. That way, there is no injection of the ModelState to the service - the service just does validation and returns any errors. It is then up to the controller to merge these ModelState errors back into it's ViewData.

So the service validation method might look like:

public IDictionary<string, string> ValidatePerson(Person person)
{
Dictionary<string, string> errors = new Dictionary<string, string>();

// Do some validation, e.g. check if the person already exists etc etc

// Add model erros e.g.:
errors.Add("Email", "This person already exists");
}

And then you could use an extension method in your controller to map these errors onto the ModelState, something like:

public static class ModelStateDictionaryExtensions
{
public static void Merge(this ModelStateDictionary modelState, IDictionary<string, string> dictionary, string prefix)
{
foreach (var item in dictionary)
{
modelState.AddModelError((string.IsNullOrEmpty(prefix) ? "" : (prefix + ".")) + item.Key, item.Value);
}
}
}

And your controller would then use:

ModelState.Merge(personService.ValidatePerson(person), "");

Asp.net Core AutoFac register generic using factory

Autofac has a great feature that enables us to register factories to create instances based on a parameter(s). In your example, we could register a Func<Type, IValidator> with Autofac, and have that automagically injected into our ValidationProvider.

var builder = new ContainerBuilder();
builder
//register our factory function
.Register<Func<Type, IValidator>>(
x =>
{
//get a reference to the scoped container
//e.g. if this is a web app, each HTTP request will
//spawn a child container used for the lifetime of that request
var context = x.Resolve<IComponentContext>();
return type =>
{
//create the validator from our scoped container
var valType = typeof(Validator<>).MakeGenericType(type);
return (IValidator) context.Resolve(valType);
}
}
)};

public class ValidationProvider
{
readonly Func<Type, IValidator> _factory;
//Autofac will see this class requires our previously registered
//function and inject this for us
public ValidationProvider(Func<Type, IValidator> factory)
{
_factory = factory;
}
}

As an alternative, is it possible for you to constrain the IValidator with a generic argument? Perhaps it is not feasible to refactor the code, but if it is, it may be better practice to give our services the exact dependencies they require, rather than a factory which may hide their intent.

public interface IValidator<T>
{
void Validate(T instance);
}

public class SomeClassRequiringAudioModelValidator
{
readonly IValidator<AudioModel> _validator;
public SomeClassRequiringAudioModelValidator(IValidator<AudioModel> validator)
{
_validator = validator;
}
}

Using Ninject to inject current user into my domain

I'm assuming you're using the NinjectHttpApplication since you have overridden OnApplicationStarted? If not, you should be, since it will register the Controllers for you with Ninject. If that's not happening, then you can get the error you are seeing.

Here's how I have done this in my application:

ControllerBase:

    [Inject]
public IMembershipProvider Membership { get; set; }

public Member CurrentMember
{
get { return Membership.GetCurrentUser() as Member; }
}

IMembershipProvider:

    public Member GetCurrentUser()
{
if ( string.IsNullOrEmpty( authentication.CurrentUserName ) )
return null;

if ( _currentUser == null )
{
_currentUser = repository.GetByName( authentication.CurrentUserName );
}
return _currentUser;
}
private Member _currentUser;

IAuthenticationProvider:

    public string CurrentUserName
{
get
{
var context = HttpContext.Current;
if ( context != null && context.User != null && context.User.Identity != null )
return HttpContext.Current.User.Identity.Name;
else
return null;
}
}

let me know if this doesn't make sense.

How to inject dependency in same activation procedure from already resolved instance?

No you can't

To see the problem you must think about the order in which the objects are created

var modelStateWrapper = new ModelStateWrapper();
var service = new ContactService(modelStateWrapper);
var controller = new ContactController(service);

This means the modelstate wrapper is created longtime before the controller and therefore it is impossible to pass the model state to the ModelStateWrapper's constructor. The only thing that is doable is to use Property Injection somewhere but this is only a workaround for the actual problem which is that you have a cyclic dependency.

The implementation also ties the service tightly to the controller. Consider using ModelValidators instead.

How do i use dependency injection correct?

If you note the last part of my answer on your previous question, you can also just pass the reference to ModelState directly through the IAccountService interface. In this case, that would simplify things, but given the lack of context in your previous question it was unclear which would be the better choice.

public class AccountController : Controller
{
private readonly ILanguageService languageService;
private readonly ISessionHelper sessionHelper;
private readonly IAccountService accountService;

public AccountController(ILanguageService languageService, ISessionHelper sessionHelper, IAccountService accountService)
{
if (languageService == null)
throw new ArgumentNullException("languageService");
if (sessionHelper == null)
throw new ArgumentNullException("sessionHelper");
if (accountService == null)
throw new ArgumentNullException("accountService");

this.languageService = languageService;
this.sessionHelper = sessionHelper;
this.accountService = sessionHelper;
}

[HttpPost]
public ActionResult PostSomething()
{

// Pass model state of the current request to the service.
this.accountService.DoSomething(new ModelStateWrapper(this.ModelState));

}
}

This is clearly easier than injecting 40 factories, and would make your DI configuration simpler.

The only downside of this approach (if you can call it that) is that your IAccountService interface now requires an implemenatation of IValidationDictionary to be passed. This means that every concrete implementation of IAccountService must have a dependency on ModelState's abstraction, IValidationDictionary. If that is the expectation of the design, then that is fine.

Also, you should put guard clauses in your class to ensure that if the DI container fails to provide an instance an exception will be thrown. The readonly keyword in the private fields makes it impossible to change the value of those fields outside of the constructor. Basically, these 2 things combined guarantee that you will have an instance that is initialized in the constructor and will remain constant throughout the lifetime of the object.

Final Thoughts

DI is by its nature complex. If you want to get a good answer, you should provide more context about the problem. I realize that SO says that you should post the minimum required code, but questions about DI generally require more context to answer than other questions because of its complexity.

Had I known you were trying to solve a problem for 40 similar services or had known about the relevant part of the IAccountService members that use the dependency (which you still haven't provided), my answer probably would have been different. It could still change if you provide more context, but there are only so many assumptions I can make about what you are trying to achieve. It helps a lot if you fill in the blanks.



Related Topics



Leave a reply



Submit