What is the proper way of using Ninject convention based binding?

Shubhajyoti Ghosh picture Shubhajyoti Ghosh · Feb 28, 2014 · Viewed 7.1k times · Source

I use Ninject mainly as manual binding like bellow. Which is working properly

kernel.Bind<TestContext>().ToSelf().InRequestScope();
kernel.Bind<ITestRepository>().To<TestRepository>();

But when I try bind using Convention based binding, bit confused about, what to use when ?

I go through this Ninject Documentation But I unable to found much example.

As per my all Repository class Implement IRepository < Model >. So if I want do binding in conventional way, then bellow code working properly.

      kernel.Bind(x => x
            .FromAssembliesMatching("*")
            .SelectAllClasses()
            .InheritedFrom(typeof(IRepository<>))
            .BindDefaultInterface());

But I am bit confused

1. when I changed .FromAssembliesMatching("*") To .FromThisAssembly() it is not working properly, and throw Error activating ITestRepository Why ?

2. And also when changed .SelectAllClasses() To .SelectAllIncludingAbstractClasses() with combination of .FromAssembliesMatching("*") it is work properly, why ?

Let me explain my code structure.

IRepository (within DLL 1)

public interface IRepository<E>
{
    E Get();
}

RepositoryBase (within DLL 1)

 public abstract class RepositoryBase<E> : IRepository<E>
    where E : class
{

    public E Get()
    {
        return System.Activator.CreateInstance<E>(); // this is just for testing
    }
}

TestRepository (Within DLL 2)

public  interface ITestRepository : IRepository<TestModel>
{

}

public class TestRepository : RepositoryBase<TestModel>, ITestRepository
{

}

Now withing Controller (Within DLL 3)

    private readonly ITestRepository _testRepository;

    public HomeController(ITestRepository testRepository)
    {
        _testRepository = testRepository;
    }

Please tell me what is best way to do it via conventional binding ?

Note DLL 1, 2, 3 means different project

Additional

Error Details

Server Error in '/' Application.

Error activating ITestRepository
No matching bindings are available, and the type is not self-bindable.
Activation path:
 2) Injection of dependency ITestRepository into parameter testRepository of constructor of type HomeController
 1) Request for HomeController

Suggestions:
 1) Ensure that you have defined a binding for ITestRepository.
 2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
 3) Ensure you have not accidentally created more than one kernel.
 4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
 5) If you are using automatic module loading, ensure the search path and filters are correct.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: Ninject.ActivationException: Error activating ITestRepository
No matching bindings are available, and the type is not self-bindable.
Activation path:
 2) Injection of dependency ITestRepository into parameter testRepository of constructor of type HomeController
 1) Request for HomeController

Suggestions:
 1) Ensure that you have defined a binding for ITestRepository.
 2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
 3) Ensure you have not accidentally created more than one kernel.
 4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
 5) If you are using automatic module loading, ensure the search path and filters are correct.


Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[ActivationException: Error activating ITestRepository
No matching bindings are available, and the type is not self-bindable.
Activation path:
  2) Injection of dependency ITestRepository into parameter testRepository of constructor of type HomeController
  1) Request for HomeController

Suggestions:
  1) Ensure that you have defined a binding for ITestRepository.
  2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
  3) Ensure you have not accidentally created more than one kernel.
  4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
  5) If you are using automatic module loading, ensure the search path and filters are correct.
]
   Ninject.KernelBase.Resolve(IRequest request) in c:\Projects\Ninject\ninject\src\Ninject\KernelBase.cs:359
   Ninject.Planning.Targets.Target`1.GetValue(Type service, IContext parent) in c:\Projects\Ninject\ninject\src\Ninject\Planning\Targets\Target.cs:197
   Ninject.Planning.Targets.Target`1.ResolveWithin(IContext parent) in c:\Projects\Ninject\ninject\src\Ninject\Planning\Targets\Target.cs:165
   Ninject.Activation.Providers.StandardProvider.GetValue(IContext context, ITarget target) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:114
   Ninject.Activation.Providers.<>c__DisplayClass4.<Create>b__2(ITarget target) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:96
   System.Linq.WhereSelectArrayIterator`2.MoveNext() +66
   System.Linq.Buffer`1..ctor(IEnumerable`1 source) +216
   System.Linq.Enumerable.ToArray(IEnumerable`1 source) +77
   Ninject.Activation.Providers.StandardProvider.Create(IContext context) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:96
   Ninject.Activation.Context.Resolve() in c:\Projects\Ninject\ninject\src\Ninject\Activation\Context.cs:157
   Ninject.<>c__DisplayClass10.<Resolve>b__c(IBinding binding) in c:\Projects\Ninject\ninject\src\Ninject\KernelBase.cs:386
   System.Linq.WhereSelectEnumerableIterator`2.MoveNext() +145
   System.Linq.Enumerable.SingleOrDefault(IEnumerable`1 source) +4098209
   Ninject.Web.Mvc.NinjectDependencyResolver.GetService(Type serviceType) in c:\Projects\Ninject\ninject.web.mvc\mvc3\src\Ninject.Web.Mvc\NinjectDependencyResolver.cs:56
   System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +41

[InvalidOperationException: An error occurred when trying to create a controller of type 'MVCPluginApproach.Controllers.HomeController'. Make sure that the controller has a parameterless public constructor.]
   System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +179
   System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) +80
   System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName) +74
   System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +197
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +49
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +50
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +301
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

** As per my basic understanding... cause of the error multiple assembly, instated of a single assembly. So when I try to bind it via .FromThisAssembly() then Ninject unable resolve it. Is it true ?

Answer

War picture War · Mar 6, 2014

Here's how I do it with Ninject 3.0 ...

//binding rules in all assemblies named "My.<anything>.dll"
kernel.Bind(x => x.FromAssembliesMatching("My.*.dll")
                    .SelectAllClasses()
                    .BindAllInterfaces()
            );

// basic interface
ISomething<T> { /* code for interface */ }

// implementation of interface
Something<T> { /* implement interface */ }

// get an instance of Something<AnyType> by asking for ISomething<AnyType>
var something = kernel.Get<ISomething<SomeType>>();

Then all you need to do is declare more interfaces and classes.

When you want to use abstract classes / inheritance the simplest way without complicating your rules is to do something like this ...

ICustomSomething : ISomething<SomeType> { /* extended interface parts */ }

CustomSomething : Something<SomeType>, ICustomSomething { /* implement ICustomSomething */ }

No new dependency rules needed. The convention used here basically means that Ninjects core will look for I... and then match a type to it by looking for type with the same name minus the I prefix as a general rule of thumb so for each new class / type you define simply declare an interface for it and ninject needs no more information.

If you want to do more complex stuff (like say polymorphic scenarios) then I would suggest having ninject create a factory which you give your type to and have that spit out the right type from that.

so for example you could have ...

kernel.Bind<ISomething<>>().ToMethod(() => () { kernel.Get<SomeFactory>().Get<T>() });

Sorry if the code here isn't exactly spot on it's all from the ram in my head but you should be able to get the idea from it :)

EDIT: Observation ... It's probably worth noting that this often won't work :

kernel.Bind(x => x.FromAssembliesMatching("*") ...);

The directive ".FromAssembliesMatching("*")" will literally pick up all assemblies in your bin folder and try to build rules for them and I often find some third parties create assemblies that can't be treated this way, you also may have chunks of .Net in your bin folder which you don't want to be building rules for.

I hit some brick walls doing that and decided to create my own assembly naming convention so I have stuff like ...

MyCorp.Core.dll; MyCorp.Common.dll; MyCorp.AppName.Repositories.dll; MyCorp.AppName.Services.dll;

so when I bind I only bind these assemblies not everything using

.FromAssembliesMatching("MyCorp.*.dll")

Other than that I found very little problems with Ninject, it's pretty basic stuff !!