使用Ninject.Web.Mvc 2.0和ASP.NET MVC 1.0时出现“多个匹配的绑定可用”错误

时间:2010-03-11 09:34:52

标签: asp.net-mvc ninject

最近我转到Ninject 2.0版本并开始收到以下错误:

Error occured: Error activating SomeController
More than one matching bindings are available.
Activation path:
  1) Request for SomeController

Suggestions:
  1) Ensure that you have defined a binding for SomeController only once.

但是,我找不到某种复制路径。有时它会发生,有时它不会发生。 我正在使用NinjectHttpApplication进行自动控制器注入。控制器在单独的程序集中定义:

public class App : NinjectHttpApplication
{
    protected override IKernel CreateKernel()
    {
        INinjectModule[] modules = new INinjectModule[] {
            new MiscModule(),
            new ProvidersModule(),
            new RepositoryModule(),
            new ServiceModule()
        };

        return new StandardKernel(modules);
    }

    protected override void OnApplicationStarted()
    {
        RegisterRoutes(RouteTable.Routes);
        RegisterAllControllersIn("Sample.Mvc");
        base.OnApplicationStarted();
    }

    /* ............. */

}

也许有人熟悉这个错误。

有什么建议吗?

5 个答案:

答案 0 :(得分:23)

我最近最终想出了这个问题。显然,NinjectHttpApplication.RegisterAllControllersIn()函数不会执行所需的所有正确绑定。它将您的具体控制器实现绑定到IController请求。例如,如果您有一个名为SampleMvcController的控制器类,它继承自System.Web.Mvc.Controller。它将在应用程序启动期间执行以下命名绑定:

kernel.Bind<IController>().To(SampleMvcController).InTransientScope().Named("SampleMvc");

但是在调试NinjectControllerFactory时,我发现正在为Ninject内核发出请求,要求使用“SampleMvc”的命名绑定返回“SampleMvcController”类的对象,而不是IController的具体实现。

因此,当涉及SampleMvcController的第一个Web请求生成时,它会创建SampleMvcController与其自身的绑定。但这不是线程安全的。因此,如果您同时发出多个Web请求,则绑定可能会多次发生,现在您因为为SampleMvcController进行多次绑定而留下此错误。

您可以在重新启动Web应用程序后立即通过快速刷新MVC URL来验证这一点。

修复:

解决此问题的最简单方法是为控制器绑定创建新的NinjectModule,并在应用程序启动期间加载此模块。在此模块中,您将自动绑定每个已定义的控制器,如下所示:

class ControllerModule : StandardModule {
      public override Load() {
        Bind<SampleMvcController>().ToSelf();
        Bind<AnotherMvcController>().ToSelf();
      }
    }

但如果您不介意更改Ninject源代码,可以修改RegisterAllControllersIn()函数以自动绑定它遇到的每个控制器。

答案 1 :(得分:16)

我几个月来一直在处理这个问题。我尝试了很多选择,但无法找到解决方案。我知道这是一个线程问题,因为只有当我的网站负载很重时才会发生。就在最近,在ninject源代码中报告并修复了一个错误,解决了这个问题。

以下是对issue的引用。它在Ninject源的2.1.0.70版中得到修复。通过删除行

,关键更改位于KernelBase.cs
context.Plan = planner.GetPlan(service);

并将其替换为

lock (planner)
{
    context.Plan = planner.GetPlan(service);
}

要在MVC中使用此新版本,您需要获取Ninject的最新版本,然后获取ninject.web.mvc的最新版本。使用新的Ninject构建构建ninject.web.mvc。

我一直在使用这个新版本大约一个星期,负载很重,没有问题。这是它没有问题的最长时间,所以我认为这是一个解决方案。

答案 2 :(得分:1)

您确定每次调用Kernel时,是否真的从头开始创建一个全新的OnApplicationStarted?如果你不是,你实际上是在创建它一次,但可能会运行两次注册位。请记住,您不能保证在给定的AppDomain中只实例化一个App类。

答案 3 :(得分:1)

我的回答更加明显。

在我的代码重构期间,我已经多次声明了我的一个控制器的绑定。

答案 4 :(得分:0)

我将此添加到我的global.ascx.cs文件中:

        public void RegisterAllControllersInFix(Assembly assembly)
    {
        RegisterAllControllersInFix(assembly, GetControllerName);
    }

    public void RegisterAllControllersInFix(Assembly assembly, Func<Type, string> namingConvention)
    {
        foreach (Type type in assembly.GetExportedTypes().Where(IsController))
            Kernel.Bind(type).ToSelf();
    }

    private static bool IsController(Type type)
    {
        return typeof(IController).IsAssignableFrom(type) && type.IsPublic && !type.IsAbstract && !type.IsInterface;
    }

    private static string GetControllerName(Type type)
    {
        string name = type.Name.ToLowerInvariant();

        if (name.EndsWith("controller"))
            name = name.Substring(0, name.IndexOf("controller"));

        return name;
    }

然后从我的OnApplicationStarted()方法调用它,如下所示:

        RegisterAllControllersIn(Assembly.GetExecutingAssembly());
        RegisterAllControllersInFix(Assembly.GetExecutingAssembly());

很难知道这是否能解决这个问题,因为它是间歇性的。