Certifique-se de que o controlador tenha um erro de construtor público sem parâmetros

105

Eu segui este tutorial que funcionou muito bem, até que modifiquei meu DbContextpara ter um construtor adicional. Agora estou tendo problemas com a resolução e não tenho certeza do que fazer para corrigir isso. Existe uma maneira fácil de forçá-lo a agarrar o construtor sem parâmetros ou estou abordando isso incorretamente?

DbContext com dois construtores:

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

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

SiteController construtor:

private readonly IDashboardRepository _repo;

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

Repositório:

DashboardDbContext _context;

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

UnityResolver código:

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);

Erro da chamada WebApi:

System.InvalidOperationException: Ocorreu um erro ao tentar criar um controlador do tipo 'SiteController'. Certifique-se de que o controlador tenha um construtor público sem parâmetros.

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: Tipo 'Dashboard.Web.Controllers.SiteController' não tem um construtor padrão.

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)

O tutorial foi ótimo e tem funcionado bem para mim até eu adicionar o segundo construtor.

Scarpacci
fonte
2
O erro está dizendo que SiteControlleré isso que deve ter um construtor sem parâmetros, não DashboardDbContext.
Neil Smith
Oi Smith.h.Neil, mas só lança esse erro quando o construtor adicional é adicionado ao dbcontext. Se eu remover ou comentar (segundo construtor), funcionará bem.
scarpacci
Posso ver o construtor para SiteController?
Neil Smith
E suponho que você está injetando DbContextno repositório?
Neil Smith de
@scarpacci Tem certeza de que a única alteração que você está fazendo é remover o segundo construtor do DbContext? A menos que você esteja de alguma forma contornando a instanciação de seu controlador por não ter o segundo construtor DbContext, não faria sentido que o erro fosse dependente dos construtores DbContext.
Asad Saeeduddin de

Respostas:

130

O que está acontecendo é que você está mordido por esse problema . Basicamente, o que aconteceu é que você não registrou seus controladores explicitamente em seu contêiner. O Unity tenta resolver os tipos concretos não registrados para você, mas como não consegue resolver (causado por um erro em sua configuração), ele retorna nulo. Ele é forçado a retornar nulo, porque a API da Web o força a fazê-lo devido ao IDependencyResolvercontrato. Como o Unity retorna nulo, a API Web tentará criar o próprio controlador, mas, como não tem um construtor padrão, lançará a exceção "Certifique-se de que o controlador possui um construtor público sem parâmetros". Essa mensagem de exceção é enganosa e não explica a causa real.

Você teria visto uma mensagem de exceção muito mais clara se registrasse seus controladores explicitamente, e é por isso que você deve sempre registrar todos os tipos de raiz explicitamente.

Mas é claro, o erro de configuração vem de você adicionar o segundo construtor ao seu DbContext. O Unity sempre tenta escolher o construtor com mais argumentos, mas não tem ideia de como resolver esse construtor específico.

Portanto, a verdadeira causa é que você está tentando usar os recursos de fiação automática do Unity para criar o DbContext. DbContexté um tipo especial que não deve ser conectado automaticamente. É um tipo de estrutura e você deve, portanto, voltar a registrá-lo usando um representante de fábrica :

container.Register<DashboardDbContext>(
    new InjectionFactory(c => new DashboardDbContext())); 
Steven
fonte
OBSERVE que quando você reconstruir seu projeto, você pode redefinir suas credenciais de login ... antes de tentar aplicar esta solução, por favor: reconstrua seu projeto, faça logout e, em seguida, entre novamente, somente então - atualize sua página e observe se o problema persiste
ymz 01 de
Obrigado - esses dingleberries em minha equipe de back-end quebram muitas regras com as configurações de unidade. Começo a me perguntar se é assim que toda equipe usa contêineres IOC.
Dagrooms
@Dagrooms: Muitos desenvolvedores se incomodam com isso, mas este não é um problema que existe em todos os DI Containers. O Simple Injector por exemplo, sempre garantirá que um erro expressivo seja acionado caso tal coisa aconteça. Outra boa dica: não use um personalizado, IDependencyResolvermas apenas um personalizado IControllerActivator.
Steven
46

No meu caso, foi por causa de uma exceção dentro do construtor da minha dependência injetada (em seu exemplo - dentro do construtor DashboardRepository). A exceção foi detectada em algum lugar dentro da infraestrutura MVC. Eu descobri isso depois de adicionar logs em locais relevantes.

Illidan
fonte
7
Esta é uma resposta muito importante. É muito fácil cair na armadilha de perseguir problemas de configuração com o Unity ao ver, Make sure that the controller has a parameterless public constructor.mas é bem possível que a dependência esteja configurada, mas uma exceção nas entranhas impediu que isso fosse resolvido.
Phil Cooper,
2
Este. Um milhão de vezes! Esqueci de adicionar um mapa de dependência à configuração do Ninject.
Travo
Minha exceção nas entranhas era um tipo de propriedade de 'string' quando deveria ser 'DateTime?' Não teria procurado isso se não tivesse visto essa resposta. Muito obrigado.
Jazzy de
Mais como LousyErrorMessageException ()
Simon_Weaver
Em quais lugares relevantes você colocou o log? Eu executei do depurador, mas não tive exceção, mesmo quando verifiquei todas as exceções CLR. Tive que adicionar a resolução do construtor manual e só então recebi o erro. Ele me disse para adicionar Diagnóstico para obter um erro utilizável, que finalmente me deu algo com que trabalhar
Arjan
6

Eu tive o mesmo problema e resolvi fazendo alterações no arquivo UnityConfig.cs. Para resolver o problema de dependência no arquivo UnityConfig.cs, você deve adicionar:

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

Às vezes, porque você está resolvendo sua interface em ContainerBootstraper.cs, é muito difícil detectar o erro. No meu caso, ocorreu um erro ao resolver a implementação da interface que injetei no controlador da API. Não consegui encontrar o erro porque resolvi a interface em meu bootstraperContainer assim: container.RegisterType<IInterfaceApi, MyInterfaceImplementaionHelper>(new ContainerControlledLifetimeManager());
então adicionei a seguinte linha em meu contêiner de bootstrap: container.RegisterType<MyController>(); então, quando eu compilar o projeto, o compilador reclamou e parou na linha acima e mostrou o erro .

Amir978
fonte
4

Eu tive o mesmo problema. Pesquisei no Google por dois dias. Por fim notei sem querer que o problema era modificador de acesso do construtor do Controller. Não coloquei a publicpalavra-chave por trás do construtor do Controlador.

public class MyController : ApiController
    {
        private readonly IMyClass _myClass;

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

Acrescento esta experiência como outra resposta, talvez outra pessoa tenha cometido um erro semelhante.

Bobs
fonte
0

Se você tiver uma interface em seu controlador

public myController(IXInterface Xinstance){}

Você deve registrá-los no contêiner de injeção de dependência.

container.Bind<IXInterface>().To<XClass>().InRequestScope();
Ahmet Arslan
fonte
0

Recebi este erro quando defini acidentalmente uma propriedade como um tipo de objeto específico, em vez do tipo de interface que defini no UnityContainer.

Por exemplo:

Definindo UnityContainer:

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

SiteController (o caminho errado - observe o tipo de repo):

private readonly DashboardRepository _repo;

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

SiteController (da maneira certa):

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}
Obra-prima
fonte
0

Se você estiver usando UnityConfig.cs para resistir aos mapeamentos do seu tipo, como abaixo.

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

Você tem que informar **webApiConfig.cs**sobre o Container

config.DependencyResolver = new Unity.AspNet.WebApi.UnityDependencyResolver(UnityConfig.Container);
Vinay Patel
fonte
0

No meu caso, a Unidade acabou sendo uma pista falsa. Meu problema era resultado de diferentes projetos direcionados a diferentes versões do .NET. O Unity foi configurado corretamente e tudo foi registrado corretamente no contêiner. Tudo compilado bem. Mas o tipo estava em uma biblioteca de classes e a biblioteca de classes foi configurada para o .NET Framework 4.0. O projeto WebApi usando Unity foi definido para o .NET Framework 4.5. Alterar a biblioteca de classes para também direcionar 4.5 resolveu o problema para mim.

Eu descobri isso comentando o construtor DI e adicionando o construtor padrão. Comentei os métodos do controlador e fiz com que lançassem NotImplementedException. Confirmei que poderia alcançar o controlador e, ao ver minha NotImplementedException, me disse que estava instanciando o controlador corretamente. Em seguida, no construtor padrão, instanciei manualmente a cadeia de dependência em vez de depender do Unity. Ele ainda compilou, mas quando o executei, a mensagem de erro voltou. Isso confirmou para mim que ainda recebia o erro mesmo quando o Unity estava fora de cogitação. Por fim, comecei na parte inferior da corrente e fui subindo, comentando uma linha de cada vez e testando novamente até não receber mais a mensagem de erro. Isso me apontou na direção da classe ofensiva e, a partir daí, descobri que ela estava isolada em uma única assembléia.

Charlie Kilian
fonte