c# - tutorial - Inyector simple que no puede inyectar dependencias en los controladores de API web
dependency injection web api 2 (1)
TLTR: el problema es causado por la forma implícita en que la API Web maneja la resolución de los tipos de controlador; registre sus controladores API web explícitamente y verá dónde está el problema.
Aquí hay un paso a paso de lo que está sucediendo bajo las sábanas:
- El
System.Web.Http.DefaultHttpControllerActivator
llama alSimpleInjectorWebApiDependencyResolver
y solicita la creación de un controlador API. -
SimpleInjectorWebApiDependencyResolver
reenvía esa llamada a la instanciaSimpleInjector.Container
. - Sin embargo, esa instancia de
Container
no tiene ningún registro explícito para ese controlador de API (ya que proporcionó un contenedor vacío al resolver). - Como no hay un registro explícito, el contenedor intenta hacer un registro de última hora para ese tipo.
- Sin embargo, ese tipo de controlador depende de las interfaces que no se pueden resolver porque no están registradas en el contenedor (recuerde que su contenedor está vacío).
- Aunque el contenedor normalmente lanzaría una excepción, en este caso se devuelve nulo, porque el tipo se solicita a través del método
IServiceProvider.GetService
y el tipo no se registró explícitamente. - El método
GetService
devolveránull
también, ya que es por definición que debe devolver nulo; Debería devolver nulo cuando no existe registro (que actualmente es el caso). - Como
DependencyResolver
devolvió nulo,DefaultHttpControllerActivator
volverá a su comportamiento predeterminado, lo que significa queDefaultHttpControllerActivator
ese tipo en sí mismo, pero esto requiere que el controlador tenga un constructor predeterminado.
En pocas palabras, el problema es causado por la forma implícita en que la API Web maneja la resolución de los tipos de controladores.
Entonces la solución aquí es:
- Tener solo un
Container
único en su aplicación web. Esto evita todo tipo de problemas y complicaciones de su configuración. - Registre todos los Controladores Web API explícitamente en el contenedor. El registro explícito de los controladores garantizará que Simple Injector arroje una excepción cuando no se pueda resolver un controlador. Además, esto le permite llamar a
container.Verify()
que hará que la aplicación falle durante el inicio cuando la configuración no es válida (una configuración verificable es importante ). Y esto también le permite diagnosticar la configuración que le brinda aún más confianza sobre la exactitud de su configuración.
Mi consejo es colocar MVC y Web API en su propio proyecto . Esto hará las cosas mucho más fáciles.
El registro de todos los controladores de API web se puede hacer con el siguiente código:
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
ACTUALIZAR:
Debido a que este error es tan común, las versiones más nuevas de la clase SimpleInjectorWebApiDependencyResolver
simplemente nunca devolverán null
cuando se solicite un tipo de controlador. En su lugar arrojará un error descriptivo. Debido a esto, nunca más SimpleInjectorWebApiDependencyResolver
ver el error, siempre y cuando use SimpleInjectorWebApiDependencyResolver
.
Estoy intentando hacer un constructor básico DI con Simple Injector, y parece que no puede resolver las dependencias para los controladores de API web.
- Tengo un controlador API en una carpeta "API", que está fuera de la carpeta "Controladores".
- También intenté colocarlo dentro de la carpeta "Controladores", pero eso no pareció marcar una gran diferencia. La traza de pila que recibo es similar a la presentada en esta pregunta .
- Estoy utilizando una nueva instalación del paquete NuGet "Simple Injector MVC Integration Quick Start" (v. 2.1.0).
- Tengo la base
SimpleInjectorWebApiDependencyResolver
de la documentación, que también es la misma que here . - Estoy usando Entity Framework, y he visto el hilo de discusión sobre los cambios para cargar correctamente el contexto.
Esto no parece ser un problema, pero aún recibo el siguiente error:
Escriba ''MyProject.API.ArticleController'' no tiene un constructor predeterminado
System.ArgumentException at
System.Linq.Expressions.Expression.New (Tipo de tipo) en System.Web.Http.Internal.TypeActivator.Create [TBase] (Tipo instanceType) en System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator (solicitud HttpRequestMessage, Tipo controllerType , Func`1 y activador) en System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create (Solicitud HttpRequestMessage, HttpControllerDescriptor controllerDescriptor, Type controllerType)
Se agradecería si alguien pudiera ofrecerme algunas sugerencias sobre si algo debe ser modificado a partir de su orden actual de estado / llamada.
ArticleController (estructura básica):
public class ArticleController : ApiController
{
private readonly IArticleRepository articleRepository;
private readonly IUserRepository userRepository;
private readonly IReleaseRepository releaseRepository;
public ArticleController(IArticleRepository articleRepository, IUserRepository userRepository, IReleaseRepository releaseRepository)
{
this.articleRepository = articleRepository;
this.userRepository = userRepository;
this.releaseRepository = releaseRepository;
}
// GET api/Article
public IEnumerable<Article> GetArticles(){ // code }
// GET api/Article/5
public Article GetArticle(int id){ // code }
// PUT api/Article/5
public HttpResponseMessage PutArticle(int id, Article article){ // code }
// POST api/Article
public HttpResponseMessage PostArticle(ArticleModel article){ // code }
// DELETE api/Article/5
public HttpResponseMessage DeleteArticle(int id){ // code }
}
SimpleInjectorInitializer:
public static class SimpleInjectorInitializer
{
public static void Initialize()
{
var container = new Container();
InitializeContainer(container);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.RegisterMvcAttributeFilterProvider();
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
private static void InitializeContainer(Container container)
{
container.Register<IArticleRepository, ArticleRepository>();
container.Register<IUserRepository, UserRepository>();
container.Register<IReleaseRepository, ReleaseRepository>();
}
}
Global.asax.cs:
public class WebApiApplication : System.Web.HttpApplication
{
private void ConfigureApi()
{
// Create the container as usual.
var container = new Container();
// Verify the container configuration
// container.Verify();
// Register the dependency resolver.
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ConfigureApi();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}