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

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));
}
befree2j
источник
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>(); поэтому, когда я компилирую проект, компилятор пожаловался и остановился в строке выше и показал ошибку .

Amir978
источник
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 не используется. Наконец, я начал с нижней части цепочки и продвигался вверх, комментируя одну строку за раз и повторно тестируя, пока не перестану получать сообщение об ошибке. Это указывало мне на проблемный класс, и оттуда я понял, что он изолирован от одной сборки.

Кэти Килиан
источник