I am attempting to do some basic constructor DI with Simple Injector, and it seems that it is unable to resolve the dependencies for Web API controllers.
SimpleInjectorWebApiDependencyResolver
from the documentation, which is also the same as found here.This does not seem to be a problem, but I still receive the following error:
Type 'MyProject.API.ArticleController' does not have a default constructor
System.ArgumentException 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)
It would be appreciated if someone could offer me some suggestions, on whether anything should be modified from its current state/call order.
ArticleController (basic structure):
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);
}
}
TLTR: the problem is caused by the implicit way Web API handles resolving controller types; register your Web API controllers explicitly and you'll see where the problem is.
Here is a step by step what is happening under the covers:
System.Web.Http.DefaultHttpControllerActivator
calls into the SimpleInjectorWebApiDependencyResolver
and requests the creation of an API controller.SimpleInjectorWebApiDependencyResolver
forwards that call to the SimpleInjector.Container
instance.Container
instance however, does not have any explicit registrations for that API Controller (since you supplied an empty container to the resolver).IServiceProvider.GetService
method and the type was not registered explictly.SimpleInjectorWebApiDependencyResolver
's GetService
method will return null
as well, since it's by definition that it should return null; It should return null when no registration exists (which currently is the case).DependencyResolver
returned null, DefaultHttpControllerActivator
will fall back to its default behavior, which means creating that type itself, but this requires the controller to have a default constructor.Long story short, the problem is caused by the implicit way Web API handles resolving controller types.
So the solution here is to:
Container
in your web application. This prevents all sorts of trouble and complication of your configuration.container.Verify()
which will make the application fail during startup when the configuration is invalid (a verifiable configuration is important). And this also allows you to diagnose the configuration which gives you even more confidence about the correctness of your configuration. My advice is to place MVC and Web API in their own project. This will make things much easier.
Registering all Web API controllers can be done with the following code:
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
UPDATE:
Because this error is so common, newer versions of the SimpleInjectorWebApiDependencyResolver
class will simply never return null
when a controller type is requested. Instead it will throw a descriptive error. Because of this you should never see error anymore, as long as you use the official SimpleInjectorWebApiDependencyResolver
.