Ошибка "Доступно несколько сопоставлений привязки" при использовании 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();
}
/* ............. */
}
Возможно, кто-то знаком с этой ошибкой.
Любые советы?
Ответы
Ответ 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());
Трудно узнать, исправлено ли это, хотя это так прерывисто.