Ninject: Default & specific bindings for a Generic class
Asked Answered
L

1

6

I have an abstract class:

 public abstract class Validator<T> : IValidator

and a couple of classes that implement this class for specific purposes, e.g.

public sealed class NewsValidator : Validator<News>

Now using Ninject i want to do Dependency Injection like the following (this particular code is NOT working):

Bind<Validator<News>>().To<NewsValidator>();
Bind(typeof(Validator<>)).To(typeof(NullValidator<>));

So what I want to achieve is that

Validator<News>

Should be bound to the Class "NewsValidator", but if any other not-bound version of this class is requested, say

Validator<Article>
Validator<SomethingElse>

that should be bound to a default Class (NullValidator). Using the code used above throws an Exception, though, because it binds the Validator < News > both to the NewsValidator as well as to the NullValidator.

How could I implement this? Particular types of the generic class should be bound to individual classes. All other types of the generic class that were not explicitly bound should be bound to a default class.

Would be really glad about a couple of suggestions! Thanks!

Links answered 2/6, 2011 at 19:45 Comment(0)
F
10

You could create a custom implementation of IMissingBindingResolver.

Whenever the kernel fails to resolve a binding for a requested service it delegates to the HandleMissingBinding method (this is true for any kernel derived from KernelBase). The HandleMissingBinding method will ask every missing binding resolver if it can create a binding for the requested service. The bindings returned by the resolvers, if any, will be added to the kernel.

Note that any binding created by a missing binding resolver will be added to the kernel as an implicit binding. This could have implication on your application. For example, if you have a mixture of explicit and implicit bindings for a service, resolving these bindings, i.e. kernel.GetAll<TService>(), only resolves explicit bindings. However, if all bindings are implicit they will all be resolved.

Ninject has two standard implementations of IMissingBindingResolver:

Let's implement a custom resolver for the null validators.

public class MissingValidatorResolver : NinjectComponent, IMissingBindingResolver
{
    public IEnumerable<IBinding> Resolve(
        Multimap<Type, IBinding> bindings, IRequest request)
    {
        var service = request.Service;
        if (!typeof(IValidator).IsAssignableFrom(service))
        {
            return Enumerable.Empty<IBinding>();
        }

        var type = service.GetGenericArguments()[0];
        var validatorType = typeof(NullValidator<>).MakeGenericType(type);

        var binding = new Binding(service)
        {
            ProviderCallback = StandardProvider.GetCreationCallback(validatorType)
        };

        return new[] { binding };
    }
}

Now the following test (using xUnit.net) passes.

[Fact]
public void ShouldResolveNonBoundValidatorDerivedFromValidatorAsNullValidator()
{
    var kernel = new StandardKernel();
    kernel.Components.Add<IMissingBindingResolver, MissingValidatorResolver>();

    var validator = kernel.Get<Validator<Article>>();
    Assert.IsType<NullValidator<Article>>(validator);
}
Frenzy answered 2/6, 2011 at 22:15 Comment(1)
whoa :) Incredible, that one's working like a charm. How did you know that?! Even google doesn't really find a lot when searching for "IMissingBindingResolver". Anyway, that's absolutely perfect, thanks a lot!!Links

© 2022 - 2024 — McMap. All rights reserved.