Подтвердить что ты не робот

Ошибка "Доступно несколько сопоставлений привязки" при использовании Ninject.Web.Mvc 2.0 и ASP.NET MVC 1.0

Недавно я переключился на выпуск 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();
    }

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

}

Возможно, кто-то знаком с этой ошибкой.

Любые советы?

4b9b3361

Ответ 1

Я, наконец, решил эту проблему недавно. По-видимому, функция 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() для привязки каждого контроллера, с которым он сталкивается.

Ответ 2

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

Вот ссылка на issue. Он был исправлен в сборке 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.

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

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