Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ninject: Default & specific bindings for a Generic class

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!

like image 644
Oliver Avatar asked Jun 02 '11 19:06

Oliver


People also ask

Why Ninject is used?

Ninject is a lightweight dependency injection framework for . NET applications. It helps you split your application into a collection of loosely-coupled, highly-cohesive pieces, and then glue them back together in a flexible manner.

How to implement dependency injection in c# using Ninject?

Step 1: We are creating an instance of Class StandardKernel. Step 2: Then we will load the Kernel. Step 3: Get the instance of the specific service that we want to inject. Step 4: Then inject the dependency.

What is a ninject module?

The Ninject modules are the tools used to register the various types with the IoC container. The advantage is that these modules are then kept in their own classes. This allows you to put different tiers/services in their own modules.


1 Answers

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:

  • DefaultValueBindingResolver
  • SelfBindingResolver

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);
}
like image 142
mrydengren Avatar answered Oct 12 '22 16:10

mrydengren