Ошибка «Доступно несколько сопоставлений» при использовании Ninject.Web.Mvc 2.0 и ASP.NET MVC 1.0 - PullRequest
30 голосов
/ 11 марта 2010

Недавно я перешел на выпуск 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 ]

23 голосов
/ 11 апреля 2010

Я наконец-то понял эту проблему недавно. По-видимому, функция NinjectHttpApplication.RegisterAllControllersIn () не выполняет всех необходимых необходимых привязок. Он связывает ваши конкретные реализации контроллера с запросами IController. Например, если у вас есть класс контроллера с именем SampleMvcController, который наследуется от System.Web.Mvc.Controller. При запуске приложения будет выполнено следующее именованное связывание:

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

Но при отладке NinjectControllerFactory я обнаружил, что делается запрос для ядра Ninject, чтобы он возвращал объект для класса "SampleMvcController", а не для конкретной реализации IController, используя именованную привязку "SampleMvc".

Из-за этого, когда выполняется первый веб-запрос, включающий SampleMvcController, он создает привязку SampleMvcController к себе. Это не потокобезопасный, хотя. Поэтому, если у вас есть несколько веб-запросов одновременно, привязки могут выполняться несколько раз, и теперь вы остаетесь с этой ошибкой, поскольку у вас есть несколько привязок для SampleMvcController.

В этом можно убедиться, быстро обновив URL-адрес MVC сразу после перезапуска веб-приложения.

Исправление:

Самый простой способ решить эту проблему - создать новый NinjectModule для привязок вашего контроллера и загрузить этот модуль во время запуска приложения. В этом модуле вы самостоятельно связываете каждый из определенных вами контроллеров, например:

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

Но если вы не возражаете против изменения исходного кода Ninject, вы можете изменить функцию RegisterAllControllersIn (), чтобы самостоятельно связывать каждый контроллер, с которым он сталкивается.

16 голосов
/ 28 октября 2010

Я занимаюсь этой проблемой уже несколько месяцев. Я пробовал так много вариантов, но не смог прийти к решению. Я знал, что это была проблема с многопоточностью, потому что она возникала только тогда, когда на моем сайте была большая нагрузка. Совсем недавно была обнаружена ошибка, исправленная в исходном коде приложения, которая решает эту проблему.

Вот ссылка на проблему . Это было исправлено в сборке 2.1.0.70 исходного кода Ninject. Смена ключа произошла в KernelBase.cs путем удаления строки

context.Plan = planner.GetPlan(service);

и замена его на

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

Чтобы использовать эту новую сборку с MVC, вам необходимо получить самую последнюю сборку Ninject , а затем получить последнюю сборку ninject.web.mvc . Создайте ninject.web.mvc с новой сборкой Ninject.

Я пользуюсь этой новой сборкой около недели с большой нагрузкой и без проблем. Это самый долгий путь без проблем, поэтому я считаю, что это решение.

1 голос
/ 18 августа 2012

Мой ответ был немного более очевидным.

Я объявил привязку для одного из моих контроллеров несколько раз во время рефакторинга моего кода.

1 голос
/ 12 марта 2010

Вы уверены, что действительно создаете один абсолютно новый Kernel с нуля в своем OnApplicationStarted каждый раз, когда он вызывается? Если нет, и вы фактически создаете его один раз, но, возможно, дважды запускаете регистрационный бит. Помните, что вам не гарантируется, что когда-либо будет создан только один App класс в данном домене приложений.

0 голосов
/ 17 августа 2010

Я добавил это в свой файл 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());

Трудно понять, исправило ли это, хотя, потому что это так неустойчиво.

...