【问题标题】:What is the proper way of using Ninject convention based binding?使用基于 Ninject 约定的绑定的正确方法是什么?
【发布时间】:2014-04-01 23:56:33
【问题描述】:

我主要使用 Ninject 作为手动绑定,如下所示。哪个工作正常

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

但是当我尝试使用基于约定的绑定进行绑定时,有点困惑,什么时候使用?

我浏览了这个Ninject Documentation 但我找不到太多例子。

根据我所有的存储库类实现IRepository &lt; Model &gt;。因此,如果我想以传统方式进行绑定,那么下面的代码可以正常工作。

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

但我有点困惑

1. 当我将.FromAssembliesMatching("*") 改为.FromThisAssembly() 时无法正常工作,并抛出Error activating ITestRepository 为什么?

2.当将.SelectAllClasses() 更改为.SelectAllIncludingAbstractClasses() 并结合.FromAssembliesMatching("*") 时,它工作正常,为什么

让我解释一下我的代码结构。

IRepository(在 DLL 1 内)

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

RepositoryBase(在 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(在 DLL 2 中)

public  interface ITestRepository : IRepository<TestModel>
{

}

public class TestRepository : RepositoryBase<TestModel>, ITestRepository
{

}

现在使用控制器(在 DLL 3 中)

    private readonly ITestRepository _testRepository;

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

请告诉我通过传统绑定最好的方法是什么?

注意 DLL 1, 2, 3 表示不同的项目

附加

错误详情

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

** 根据我的基本理解......错误的原因是多个程序集,设置为单个程序集。因此,当我尝试通过.FromThisAssembly() 绑定它时,Ninject 无法解决它。是 真的 吗?

【问题讨论】:

  • 你能给出确切的例外吗?带有消息和堆栈跟踪?还有更多文档:github.com/ninject/ninject.extensions.conventions/wiki/_pages
  • @BatteryBackupUnit :我更新了我的帖子,请看一下。
  • .FromThisAssembly() 只会绑定您定义绑定的程序集中的类。由于您在多个程序集中拥有存储库,因此仅“此程序集”的约定不适用于其他程序集。
  • .SelectAllIncludingAbstractClasses() 类呢,我应该使用它吗?
  • 不,我不这么认为。这仅在您动态生成实现时才有意义,例如作为代理。文档中也提到了这一点:github.com/ninject/ninject.extensions.conventions/wiki/…

标签: c# dependency-injection ninject ninject.web.mvc ninject-extensions


【解决方案1】:

这是我使用 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>>();

那么你需要做的就是声明更多的接口和类。

当你想使用抽象类/继承时,最简单的方法是不复杂化你的规则是做这样的事情......

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

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

不需要新的依赖规则。 这里使用的约定基本上意味着 Ninjects 核心将查找 I... 然后通过查找具有相同名称的类型减去 I 前缀作为一般经验法则来匹配类型,因此对于您定义的每个新类/类型只需为其声明一个接口,ninject 不需要更多信息。

如果你想做更复杂的事情(比如说多态场景),那么我建议让 ninject 创建一个工厂,你可以将你的类型分配给它,然后让它从中吐出正确的类型。

所以例如你可以...

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

对不起,如果这里的代码不完全正确,这都是我脑子里的东西,但你应该能够从中得到想法:)

编辑: 观察... 值得注意的是,这通常不起作用:

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

指令 ".FromAssembliesMatching("*")" 会从字面上拾取您的 bin 文件夹中的所有程序集并尝试为它们构建规则,我经常发现一些第三方创建的程序集不能以这种方式处理,您您的 bin 文件夹中也可能有大量 .Net,您不想为其构建规则。

我在这样做时碰到了一些砖墙,并决定创建自己的程序集命名约定,所以我有类似...的东西

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

所以当我绑定时,我只绑定这些程序集而不是使用

.FromAssembliesMatching("MyCorp.*.dll")

除了我发现 Ninject 的问题很少之外,它是非常基本的东西!!

【讨论】:

  • 感谢详细描述
最近更新 更多