Убедитесь, что в контроллере есть ошибка открытого конструктора без параметров.


106

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

DbContext с двумя конструкторами:

public class DashboardDbContext : DbContext
{
    public DashboardDbContext() : base("DefaultConnection") { }

    public DashboardDbContext(DbConnection dbConnection, bool owns)
        : base(dbConnection, owns) { }
}

SiteController конструктор:

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}

Репозиторий:

DashboardDbContext _context;

public DashboardRepository(DashboardDbContext context)
{
    _context = context;
}

UnityResolver код:

public class UnityResolver : IDependencyResolver
{
    private readonly IUnityContainer _container;

    public UnityResolver(IUnityContainer container)
    {
        _container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return _container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return _container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = _container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public void Dispose()
    {
        _container.Dispose();
    }
}

WebApiConfig:

var container = new UnityContainer();
container.RegisterType<IDashboardRepository, DashboardRepository>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);

Ошибка при вызове WebApi:

System.InvalidOperationException: произошла ошибка при попытке создать контроллер типа «SiteController». Убедитесь, что в контроллере есть открытый конструктор без параметров.

at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) 
at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request) 
at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) 
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__0.MoveNext()

InnerException: System.ArgumentException: Тип «Dashboard.Web.Controllers.SiteController» не имеет конструктора по умолчанию.

at System.Linq.Expressions.Expression.New(Type type) 
at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType) 
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) 
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)

Учебник был отличным и работал у меня хорошо, пока я не добавил второй конструктор.


2
Ошибка говорит вам, что SiteControllerэто то, что должно иметь конструктор без параметров, а не DashboardDbContext.
— Нил Смит

Привет, Smith.h.Neil, но он выдает эту ошибку только тогда, когда дополнительный конструктор добавляется в dbcontext. Если я удалю это или закомментирую (второй конструктор), он будет работать нормально.
— scarpacci

Могу я увидеть конструктор для SiteController?
— Нил Смит

И я предполагаю, что вы вводите DbContextв репозиторий?
— Нил Смит

@scarpacci Вы уверены, что единственное изменение, которое вы вносите, - это удаление второго конструктора из DbContext? Если вы каким-то образом не обойдете создание экземпляра вашего контроллера, не имея второго конструктора DbContext, не имело бы смысла, чтобы ошибка зависела от конструкторов DbContext.
— Asad Saeeduddin

Ответы:


130

Дело в том, что вас укусила эта проблема . По сути, произошло то, что вы явно не зарегистрировали свои контроллеры в своем контейнере. Unity пытается разрешить незарегистрированные конкретные типы за вас, но, поскольку не может решить эту проблему (из-за ошибки в вашей конфигурации), возвращает null. Он принудительно возвращает null, потому что веб-API заставляет его делать это из-за IDependencyResolverконтракта. Поскольку Unity возвращает значение null, веб-API попытается создать сам контроллер, но поскольку у него нет конструктора по умолчанию, он выдаст исключение «Убедитесь, что у контроллера есть открытый конструктор без параметров». Это сообщение об исключении вводит в заблуждение и не объясняет истинную причину.

Вы бы увидели гораздо более четкое сообщение об исключении, если бы явно зарегистрировали свои контроллеры, и поэтому вы всегда должны регистрировать все корневые типы явно.

Но, конечно, ошибка конфигурации возникает из-за того, что вы добавляете второй конструктор в свой DbContext. Unity всегда пытается выбрать конструктор с наибольшим количеством аргументов, но не знает, как разрешить этот конкретный конструктор.

Итак, настоящая причина в том, что вы пытаетесь использовать возможности автоматического подключения Unity для создания файла DbContext. DbContextэто особый тип, который не должен подключаться автоматически. Это тип фреймворка, поэтому вам следует отказаться от его регистрации с помощью фабричного делегата :

container.Register<DashboardDbContext>(
    new InjectionFactory(c => new DashboardDbContext())); 

ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ, когда вы перестраиваете свой проект, вы можете сбросить свои учетные данные для входа ... прежде чем пытаться применить это решение, пожалуйста: перестройте свой проект, выйдите из системы, затем снова войдите, только затем - обновите страницу и посмотрите, сохраняется ли проблема
— ymz 01

Спасибо - эти болваны из моей бэкэнд-команды нарушают множество правил с конфигами Unity. Начинаю задумываться, как каждая команда использует контейнеры IOC.
— Dagrooms

@Dagrooms: Многие разработчики этим не интересуются, но это не проблема, которая существует во всех контейнерах DI. Например, Simple Injector всегда будет гарантировать, что в случае возникновения такой ошибки будет предложена явная ошибка. Еще один хороший совет: не используйте обычай, IDependencyResolverа только IControllerActivatorвместо него.
— Стивен

46

В моем случае это произошло из-за исключения внутри конструктора моей внедренной зависимости (в вашем примере - внутри конструктора DashboardRepository). Исключение было обнаружено где-то внутри инфраструктуры MVC. Я обнаружил это после того, как добавил логи в соответствующие места.


7
Это действительно важный ответ. Очень легко попасть в ловушку, преследуя проблемы с настройкой Unity при просмотре, Make sure that the controller has a parameterless public constructor.но вполне возможно, что зависимость настроена, но исключение глубоко в недрах помешало ее разрешению.
— Фил Купер,

2
Это. Миллион раз! Я забыл добавить карту зависимостей в мою конфигурацию Ninject.
— Tarps

Исключением для меня, находящимся глубоко в недрах, было свойство типа «строка», тогда как оно должно было быть «DateTime?». Не стал бы этого искать, если бы не увидел этого ответа. Большое спасибо.
— Jazzy

Больше похоже на LousyErrorMessageException ()
— Simon_Weaver

В каких соответствующих местах вы разместили журнал? Я запустил отладчик, но не получил исключения, даже когда проверил все исключения CLR. Мне пришлось добавить ручное разрешение конструктора, и только тогда я получил ошибку. Он сказал мне добавить диагностику, чтобы получить работоспособную ошибку, что наконец дало мне кое-что, с чем можно было поработать
— Арьян

6

У меня была такая же проблема, и я решил ее, внеся изменения в файл UnityConfig.cs. Чтобы решить проблему зависимости в файле UnityConfig.cs, вы должны добавить:

public static void RegisterComponents()    
{
    var container = new UnityContainer();
    container.RegisterType<ITestService, TestService>();
    DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}

5

У меня такая же проблема. Два дня гуглил. Наконец я случайно заметил, что проблема была в модификаторе доступа конструктора Контроллера. Я не ставил publicключевое слово за конструктор контроллера.

public class MyController : ApiController
    {
        private readonly IMyClass _myClass;

        public MyController(IMyClass myClass)
        {
            _myClass = myClass;
        }
    }

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


4

Иногда из-за того, что вы разрешаете свой интерфейс в ContainerBootstraper.cs, очень сложно обнаружить ошибку. В моем случае произошла ошибка при разрешении реализации интерфейса, который я ввел в контроллер api. Мне не удалось найти ошибку, потому что я разрешил интерфейс в моем bootstraperContainer следующим образом: container.RegisterType<IInterfaceApi, MyInterfaceImplementaionHelper>(new ContainerControlledLifetimeManager());
затем я добавил следующую строку в свой контейнер начальной загрузки: container.RegisterType<MyController>(); поэтому, когда я компилирую проект, компилятор пожаловался и остановился в строке выше и показал ошибку .


0

Если у вас есть интерфейс в вашем контроллере

public myController(IXInterface Xinstance){}

Вы должны зарегистрировать их в контейнере внедрения зависимостей.

container.Bind<IXInterface>().To<XClass>().InRequestScope();

0

У меня возникла эта ошибка, когда я случайно определил свойство как конкретный тип объекта вместо типа интерфейса, определенного в UnityContainer.

Например:

Определение UnityContainer:

var container = new UnityContainer();
container.RegisterInstance(typeof(IDashboardRepository), DashboardRepository);
config.DependencyResolver = new UnityResolver(container);

SiteController (неправильный путь - обратите внимание на тип репо):

private readonly DashboardRepository _repo;

public SiteController(DashboardRepository repo)
{
    _repo = repo;
}

SiteController (правильный путь):

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}

0

Если вы используете UnityConfig.cs, чтобы сопротивляться сопоставлениям вашего типа, как показано ниже.

public static void RegisterTypes(IUnityContainer container)
    {
     container.RegisterType<IProductRepository, ProductRepository>();
    }

Вы должны сообщить **webApiConfig.cs**о контейнере

config.DependencyResolver = new Unity.AspNet.WebApi.UnityDependencyResolver(UnityConfig.Container);

0

В моем случае Unity оказалась отвлекающим маневром. Моя проблема возникла в результате того, что разные проекты были ориентированы на разные версии .NET. Unity был настроен правильно, и все было правильно зарегистрировано в контейнере. Все скомпилировано нормально. Но тип был в библиотеке классов, а библиотека классов была настроена на .NET Framework 4.0. Проект WebApi, использующий Unity, был настроен на .NET Framework 4.5. Изменение библиотеки классов на 4.5 также устранило проблему.

Я обнаружил это, закомментировав конструктор DI и добавив конструктор по умолчанию. Я закомментировал методы контроллера и заставил их выбросить NotImplementedException. Я подтвердил, что могу связаться с контроллером, и, увидев мое исключение NotImplementedException, сказал мне, что экземпляр контроллера работает нормально. Затем в конструкторе по умолчанию я вручную создал экземпляр цепочки зависимостей вместо того, чтобы полагаться на Unity. Он все еще скомпилирован, но когда я его запустил, сообщение об ошибке вернулось. Это подтвердило для меня, что я все еще получаю ошибку, даже когда Unity не используется. Наконец, я начал с нижней части цепочки и продвигался вверх, комментируя одну строку за раз и повторно тестируя, пока не перестану получать сообщение об ошибке. Это указывало мне на проблемный класс, и оттуда я понял, что он изолирован от одной сборки.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.