Переконайтеся, що в контролері помилка загальнодоступного конструктора, яка не має параметрів


105

Я дотримувався цього підручника, який чудово працював, поки я не змінив свою, 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. Він змушений повернути нуль, тому що Web API змушує це робити через IDependencyResolverдоговір. Оскільки Unity повертає значення null, Web API спробує створити сам контролер, але оскільки у нього немає конструктора за замовчуванням, він викине виняток "Переконайтесь, що у контролера є безпараметричний загальнодоступний конструктор". Це повідомлення про виняток є оманливим і не пояснює справжню причину.

Ви побачили б набагато чіткіше повідомлення про виключення, якби явно зареєстрували контролери, і тому ви завжди повинні чітко реєструвати всі кореневі типи.

Але, звичайно, помилка конфігурації виходить від того, що ви додаєте другий конструктор до свого DbContext. Unity завжди намагається вибрати конструктора з найбільшою кількістю аргументів, але він не має уявлення, як вирішити саме цей конструктор.

Тож справжньою причиною є те, що ви намагаєтесь використати можливості автоматичного проводки Unity для створення пристрою DbContext. DbContext- це особливий тип, який не повинен бути автоматичним. Це тип фреймворку, і тому вам слід відмовитися від реєстрації за допомогою заводського делегата :

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

ЗВЕРНІТЬ УВАГУ, коли ви відновите проект, ви можете скинути свої дані для входу ... перед тим, як спробувати застосувати це рішення, будь ласка: відновіть проект, вийдіть із системи, потім знову, лише потім - оновіть сторінку та спостерігайте, чи проблема не зникне.
ymz

Дякую - ці лозини в моїй команді бекенда порушують безліч правил із налаштуваннями єдності. Починаючи цікавитись, чи так кожна команда використовує контейнери МОК.
Dagrooms

@Dagrooms: Багато розробників це трохи, але це не проблема, яка існує у всіх контейнерах DI. Наприклад, простий інжектор завжди гарантуватиме виразну помилку у випадку, якщо таке трапиться. Ще одна гарна порада: не використовуйте звичай, IDependencyResolverа використовуйте лише звичайний IControllerActivator.
Стівен

46

У моєму випадку це було через виняток всередині конструктора моєї введеної залежності (у вашому прикладі - всередині конструктора DashboardRepository). Виняток потрапив десь у інфраструктурі MVC. Я знайшов це після того, як додав журнали у відповідні місця.


7
Це дійсно важлива відповідь. Дуже легко потрапити в пастку переслідування проблем із налаштуваннями Unity, коли бачите, Make sure that the controller has a parameterless public constructor.але цілком можливо, що залежність налаштована, але виняток у глибині кишечника зупинив її вирішення.
Філ Купер

2
Це. Мільйон разів! Я забув додати карту залежності до моєї конфігурації Ninject.
Траво

Моє виключення в глибині кишечника було властивістю типу "string", коли воно повинно було бути "DateTime?". Не шукав би цього, якби я не бачив цієї відповіді. Велике спасибі
Джазі

Більше схожий на LousyErrorMessageException ()
Simon_Weaver

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

6

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

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

4

Іноді тому, що ви вирішуєте свій інтерфейс у ContainerBootstraper.cs, дуже важко зрозуміти помилку. У моєму випадку виникла помилка при вирішенні реалізації інтерфейсу, який я вводив до контролера api. Я не зміг знайти помилку, тому що я вирішив інтерфейс у своєму bootstraperContainer, як це: container.RegisterType<IInterfaceApi, MyInterfaceImplementaionHelper>(new ContainerControlledLifetimeManager());
тоді я додав наступний рядок у свій контейнер завантажувача: container.RegisterType<MyController>(); тому коли я складав проект, компілятор поскаржився і зупинився у вище рядку та показав помилку .


4

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

public class MyController : ApiController
    {
        private readonly IMyClass _myClass;

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

Я додаю цей досвід як ще одну відповідь, можливо, хтось ще допустив подібну помилку.


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.