Asegúrese de que el controlador tenga un error de constructor público sin parámetros

105

He seguido este tutorial que ha funcionado muy bien, hasta que modifiqué mi DbContextpara tener un constructor adicional. Ahora tengo problemas con la resolución y no estoy seguro de qué hacer para solucionarlo. ¿Hay una manera fácil de forzarlo a tomar el constructor sin parámetros o me estoy acercando a esto incorrectamente?

DbContext con dos constructores:

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

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

SiteController constructor:

private readonly IDashboardRepository _repo;

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

Repositorio:

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

Error de la llamada de WebApi:

System.InvalidOperationException: se produjo un error al intentar crear un controlador de tipo 'SiteController'. Asegúrese de que el controlador tenga un constructor público sin 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: el tipo 'Dashboard.Web.Controllers.SiteController' no tiene un constructor predeterminado.

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)

El tutorial fue genial y me ha funcionado bien hasta que agregué el segundo constructor.

Scarpacci
fuente
2
El error le dice que SiteControlleres lo que debe tener un constructor sin parámetros, no DashboardDbContext.
Neil Smith
Hola Smith.h.Neil, pero solo arroja ese error cuando se agrega el constructor adicional al dbcontext. Si elimino eso o lo comento (segundo constructor), funciona bien.
scarpacci
¿Puedo ver el constructor SiteController?
Neil Smith
¿Y supongo que lo estás inyectando DbContexten el repositorio?
Neil Smith
@scarpacci ¿Está seguro de que el único cambio que está haciendo es eliminar el segundo constructor de DbContext? A menos que de alguna manera esté eludiendo la creación de instancias de su controlador al no tener el segundo constructor DbContext, no tendría sentido que el error dependa de los constructores DbContext.
Asad Saeeduddin

Respuestas:

130

Lo que pasa es que te muerde este problema . Básicamente, lo que sucedió es que no registró sus controladores explícitamente en su contenedor. Unity intenta resolver tipos concretos no registrados por usted, pero como no puede resolverlo (causado por un error en su configuración), devuelve nulo. Se ve obligado a devolver un valor nulo, porque la API web lo obliga a hacerlo debido al IDependencyResolvercontrato. Dado que Unity devuelve un valor nulo, la API web intentará crear el controlador en sí mismo, pero como no tiene un constructor predeterminado, lanzará la excepción "Asegúrese de que el controlador tenga un constructor público sin parámetros". Este mensaje de excepción es engañoso y no explica la causa real.

Habría visto un mensaje de excepción mucho más claro si registró sus controladores explícitamente, y es por eso que siempre debe registrar todos los tipos de raíz explícitamente.

Pero, por supuesto, el error de configuración proviene de que agregas el segundo constructor a tu DbContext. Unity siempre intenta elegir el constructor con más argumentos, pero no tiene idea de cómo resolver este constructor en particular.

Entonces, la causa real es que está tratando de usar las capacidades de cableado automático de Unity para crear el DbContext. DbContextes un tipo especial que no debe conectarse automáticamente. Es un tipo de marco y, por lo tanto, debe volver a registrarlo utilizando un delegado de fábrica :

container.Register<DashboardDbContext>(
    new InjectionFactory(c => new DashboardDbContext())); 
Steven
fuente
TENGA EN CUENTA que cuando reconstruya su proyecto, puede restablecer sus credenciales de inicio de sesión ... antes de intentar aplicar esta solución, por favor: reconstruya su proyecto, cierre la sesión y vuelva a ingresar, solo entonces - actualice su página y observe si el problema persiste
ymz
Gracias, estos dingleberries de mi equipo de backend rompen muchas reglas con las configuraciones de unidad. Comenzando a preguntarme si así es como todos los equipos usan los contenedores IOC.
Dagrooms
@Dagrooms: A muchos desarrolladores les molesta esto, pero este no es un problema que exista en todos los DI Containers. Simple Injector, por ejemplo, siempre se asegurará de que se solicite un error expresivo en caso de que ocurra tal cosa. Otro buen consejo: no uses una costumbre, IDependencyResolversolo usa una personalizada en su IControllerActivatorlugar.
Steven
46

En mi caso, fue debido a una excepción dentro del constructor de mi dependencia inyectada (en su ejemplo, dentro del constructor DashboardRepository). La excepción se detectó en algún lugar dentro de la infraestructura MVC. Encontré esto después de agregar registros en lugares relevantes.

Illidan
fuente
7
Esta es una respuesta realmente importante. Es muy fácil caer en la trampa de perseguir problemas de configuración con Unity al ver, Make sure that the controller has a parameterless public constructor.pero es muy posible que la dependencia esté configurada, pero una excepción en las entrañas ha impedido que se resuelva.
Phil Cooper
2
Esta. ¡Un millón de veces! Olvidé agregar un mapa de dependencia a mi configuración de Ninject.
Travo
Mi profunda excepción fue un tipo de propiedad de 'cadena' cuando debería haber sido 'DateTime?'. No habría buscado eso si no hubiera visto esta respuesta. Muchas gracias
Jazzy
Más como LousyErrorMessageException ()
Simon_Weaver
¿En qué lugares relevantes colocó el registro? Corrí desde el depurador pero no obtuve ninguna excepción, incluso cuando verifiqué todas las excepciones de CLR. Tuve que agregar la resolución manual del constructor y solo entonces recibí el error. Me dijo que agregara Diagnostic para obtener un error utilizable, que finalmente me dio algo con lo que trabajar
Arjan
6

Tuve el mismo problema y lo resolví haciendo cambios en el archivo UnityConfig.cs.Para resolver el problema de dependencia en el archivo UnityConfig.cs, debe agregar:

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

A veces, debido a que está resolviendo su interfaz en ContainerBootstraper.cs, es muy difícil detectar el error. En mi caso hubo un error al resolver la implementación de la interfaz que inyecté al controlador de la API. No pude encontrar el error porque resolví la interfaz en mi bootstraperContainer de esta manera: container.RegisterType<IInterfaceApi, MyInterfaceImplementaionHelper>(new ContainerControlledLifetimeManager());
luego agregué la siguiente línea en mi contenedor de bootstrap: container.RegisterType<MyController>(); así que cuando compilé el proyecto, el compilador se quejó y se detuvo en la línea anterior y mostró el error .

Amir978
fuente
4

Yo tuve el mismo problema. Lo busqué en Google durante dos días. Por fin noté accidentalmente que el problema era el modificador de acceso del constructor del Controlador. No puse la publicpalabra clave detrás del constructor del controlador.

public class MyController : ApiController
    {
        private readonly IMyClass _myClass;

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

Agrego esta experiencia como otra respuesta, tal vez alguien más cometió un error similar.

Bobs
fuente
0

Si tiene una interfaz en su controlador

public myController(IXInterface Xinstance){}

Debe registrarlos en el contenedor de Inyección de dependencia.

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

Tengo este error cuando definí accidentalmente una propiedad como un tipo de objeto específico, en lugar del tipo de interfaz que definí en UnityContainer.

Por ejemplo:

Definición de UnityContainer:

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

SiteController (de forma incorrecta - aviso del tipo de repositorio):

private readonly DashboardRepository _repo;

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

SiteController (la forma correcta):

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}
Obra maestra
fuente
0

Si está utilizando UnityConfig.cs para resistir las asignaciones de su tipo como se muestra a continuación.

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

Tienes que informar **webApiConfig.cs**sobre Container

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

En mi caso, Unity resultó ser una pista falsa. Mi problema fue el resultado de diferentes proyectos dirigidos a diferentes versiones de .NET. Unity se configuró correctamente y todo se registró correctamente en el contenedor. Todo compilado bien. Pero el tipo estaba en una biblioteca de clases y la biblioteca de clases estaba configurada como destino .NET Framework 4.0. El proyecto WebApi que usa Unity se configuró para .NET Framework 4.5. Cambiar la biblioteca de clases para apuntar también a 4.5 solucionó el problema para mí.

Descubrí esto comentando el constructor DI y agregando el constructor predeterminado. Comenté los métodos del controlador y les pedí que lanzaran NotImplementedException. Confirmé que podía llegar al controlador y, al ver mi NotImplementedException, me dijo que estaba creando una instancia del controlador bien. A continuación, en el constructor predeterminado, he creado una instancia manual de la cadena de dependencia en lugar de confiar en Unity. Todavía se compiló, pero cuando lo ejecuté, volvió el mensaje de error. Esto me confirmó que seguía recibiendo el error incluso cuando Unity estaba fuera de escena. Finalmente, comencé en la parte inferior de la cadena y fui subiendo, comentando una línea a la vez y volviendo a probar hasta que ya no recibí el mensaje de error. Esto me apuntó en la dirección de la clase infractora, y desde allí me di cuenta de que estaba aislada a una sola asamblea.

Charlie Kilian
fuente