10

I am trying to resolve an implementation via constructor injection, however keep getting the following:

An error occurred when trying to create a controller of type 'ApiController'. Make sure that the controller has a parameterless public constructor.

We have a mix application which is running sitecore MVC and WebApi side by side in 8.2.

  • MVC resolves dependencies just fine
  • WebApi fails to do so however

An example of our InitializeAutofacControllerFactory:

public virtual void Process(PipelineArgs args)
{
    var builder = new ContainerBuilder();

    // Register All Controllers In The Current Scope, for WebApi
    builder.RegisterApiControllers(AppDomain.CurrentDomain.GetAssemblies());

    // Register Modules
    builder.RegisterModule<ServicesModule>();

    // Register all modules in the data access project
    builder.RegisterAssemblyModules(typeof(DataAccess.Installer).Assembly);

    // Build The Container
    var container = builder.Build();

    GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}

WebApi controller, where resolution is failing:

public class ApiController : ServicesApiController
{
    private readonly IEntityAccess _entityAccess;

    public ApiController(IEntityAccess entityAccess)
    {
        _entityAccess = entityAccess;
    }

    [HttpGet]
    public IHttpActionResult Create(string title = null, string description = null)
    {
        // we do some stuff here
    }
}

Stacktrace

{
 "Message":"An error has occurred.",
 "ExceptionMessage":
 "An error occurred when trying to create a controller of type 'ApiController'. Make sure that the controller has a parameterless public constructor.",

 "ExceptionType":"System.InvalidOperationException",
 "StackTrace":
 "at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n   at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()",
 "InnerException":{"Message":"An error has occurred.",

 "ExceptionMessage":
 "Type 'NameSpace.ApiController' does not have a default constructor",
 "ExceptionType":"System.ArgumentException","StackTrace":
 "   at System.Linq.Expressions.Expression.New(Type type)\r\n   at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)\r\n   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"}
}

We've been following this implementation: https://github.com/HedgehogDevelopment/sitecore-chained-dependency-resolver/tree/feature-autofac

Patch

<?xml version="1.0"?>

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <initialize>     
        <processor type=" NameSpace.Ioc.Pipelines.InitializeAutofacControllerFactory, NameSpace.Ioc"
        patch:before="*[@type='Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc']" />
      </initialize>
    </pipelines>
  </sitecore>
</configuration>

OWIN

Not sure if this is causing it but it has also come to my attention, that we have OWIN setup following similar approach found on: https://blog.baslijten.com/how-to-add-federated-authentication-with-sitecore-and-owin/ to set up OWIN.

Trying to get an instance of IAppBuilder in the sitecore pipeline now to make the calls AutoFac recommend for using AutoFac with Owin and WebApi.

  • 1
    Where is your InitializeAutofacControllerFactory in the Sitecore pipeline? – Corey Smith Oct 24 '16 at 13:46
  • @CoreySmith added patch info – Anicho Oct 24 '16 at 18:33
  • Are you sure that there's a concrete service registered for your IEntityAccess interface? If so, put a breakpoint in the Create method of your ApiController and check that GlobalConfiguration.Configuration.DependencyResolver is indeed set to AutofacWebApiDependencyResolver. – Corey Smith Oct 24 '16 at 18:37
  • @CoreySmith yes we are sure, because it works through sitecores mvc controllers which uses the same registration. Just when we try resolve through webapi we get the problem. I will attempt to ensure the dependency resolver is in fact the web api resolver we want. – Anicho Oct 24 '16 at 18:40
  • When your pipeline executes, has the assembly containing your ApiControllers been loaded into your AppDomain? Regardless of project referencing, assemblies are loaded only when they are required. – Matt Oct 25 '16 at 22:28
6

The issue is where you're initializing the Web API DependencyResolver in the Sitecore initialize pipeline. In Sitecore 8.2, the part of the initialize pipeline where the MVC and Web API DependencyResolvers need to be registered is as follows:

Sitecore 8.2 Initialize Pipeline

<sitecore>
  <pipelines>
    <initialize>
      ...
      <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeAspNetMvc, Sitecore.Mvc" patch:source="Sitecore.Mvc.config"/>
      <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeGlobalFilters, Sitecore.Mvc" patch:source="Sitecore.Mvc.config"/>
      <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeDependencyResolver, Sitecore.Mvc" patch:source="Sitecore.Mvc.config"/>
      <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc" patch:source="Sitecore.Mvc.config"/>
      <processor type="Sitecore.Mvc.Pipelines.Initialize.InitializeCommandRoute, Sitecore.Speak.Client" patch:source="Sitecore.Speak.Mvc.config"/>
      <processor type="Sitecore.ContentTesting.Pipelines.Initialize.RegisterContentTestingCommandRoute, Sitecore.ContentTesting" patch:source="Sitecore.ContentTesting.config"/>
      <processor type="Sitecore.ContentTesting.Pipelines.Initialize.RegisterWebApiRoutes, Sitecore.ContentTesting" patch:source="Sitecore.ContentTesting.config"/>
      <processor type="Sitecore.Social.Client.Mvc.Pipelines.Initialize.RegisterSocialArea, Sitecore.Social.Client.Mvc" patch:source="Sitecore.Social.config"/>
      <processor type="Sitecore.Mvc.Pipelines.Loader.InitializeRoutes, Sitecore.Mvc" patch:source="Sitecore.Mvc.config"/>
      <processor type="Sitecore.FXM.Service.Pipelines.EnableBeaconServiceSessionStateProcessor, Sitecore.FXM.Service" patch:source="Sitecore.FXM.config"/>
      <processor type="Sitecore.Services.Infrastructure.Sitecore.Pipelines.ServicesWebApiInitializer, Sitecore.Services.Infrastructure.Sitecore" patch:source="Sitecore.Services.Client.config"/>
      <processor type="Sitecore.PathAnalyzer.Services.Pipelines.Initialize.WebApiInitializer, Sitecore.PathAnalyzer.Services" patch:source="Sitecore.PathAnalyzer.Services.config"/>
      ...
    </initialize>
  <pipelines>
</sitecore>

You're currently patching your Web API DependencyResolver initialization after Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory. While that is a perfectly valid place to initialize the MVC DependencyResolver, it is too early for changes to Web API's configuration.

For Web API, you must initialize your DependencyResolver no sooner than the Sitecore.Services.Infrastructure.Sitecore.Pipelines.ServicesWebApiInitializer processor, because this processor effectively overwrites the entire GlobalConfiguration.Configuration registry with a brand new one, erasing most configuration changes you make, including your DependencyResolver.

Additionally, if you have xDB enabled, the Sitecore.PathAnalyzer.Services.Pipelines.Initialize.WebApiInitializer processor overwrites the HttpControllerActivator for Web API, which could be a breaking change if you choose to customize it in the future. As such, best practice as of Sitecore 8.2 is to make your Web API configuration changes after the Sitecore.PathAnalyzer.Services.Pipelines.Initialize.WebApiInitializer processor.

Update your patch file as follows and you should be good to go:

Patch

<?xml version="1.0"?>

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <initialize>     
        <processor type=" NameSpace.Ioc.Pipelines.InitializeAutofacControllerFactory, NameSpace.Ioc"
        patch:after="*[@type='Sitecore.PathAnalyzer.Services.Pipelines.Initialize.WebApiInitializer, Sitecore.PathAnalyzer.Services']" />
      </initialize>
    </pipelines>
  </sitecore>
</configuration>

As an aside, if you're going to use the ChainedDependencyResolver, I recommend that you change your Autofac registration to register only your Web API Controllers, not all Controllers in the AppDomain. By registering all Controllers in the AppDomain with the Autofac container, you're effectively defeating the purpose of the ChainedDependencyResolver, which is to let your DependencyResolver (AutofacWebApiDependencyResolver) resolve your services, and let the fallback DependencyResolver resolve Sitecore's services.

Autofac Initializer

public virtual void Process(PipelineArgs args)
{
    var builder = new ContainerBuilder();

    // Register All Controllers In The Current Scope, for WebApi
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

    // Register Modules
    builder.RegisterModule<ServicesModule>();

    // Register all modules in the data access project
    builder.RegisterAssemblyModules(typeof(DataAccess.Installer).Assembly);

    // Build The Container
    var container = builder.Build();

    GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
  • 1
    Hmm, I will look into this sometime this week and let you know if your findings give me a solution. Thank you for taking the time :). – Anicho Nov 1 '16 at 12:17
4

Ran into this exact same issue but with Ninject instead. From what I could tell it was a conflict of resolution between WebAPI and MVC. Ultimately I was able to resolve the issue by implementing my own IDependencyResolver and IDependencyScope and binding that in the App_Start (suggested in https://stackoverflow.com/questions/20595472/mvc5-web-api-2-and-ninject):

public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
    private readonly IKernel _kernel;
    public NinjectDependencyResolver(IKernel kernel) : base(kernel)
    {
        _kernel = kernel;
    }
    public IDependencyScope BeginScope()
    {
        return new NinjectDependencyScope(_kernel.BeginBlock());
    }
...

and

public class NinjectDependencyScope : IDependencyScope
{
    private IResolutionRoot _resolver;
    internal NinjectDependencyScope(IResolutionRoot resolver)
    {
...

and in my App_Start //(WebActivatorEx.PreApplicationStartMethod)

public static void RegisterNinject(HttpConfiguration configuration) {
  // Set Web API Resolver
  configuration.DependencyResolver = new NinjectDependencyResolver(_bootstrapper.Kernel);
}

Now this is obviously all in Ninject, you are leveraging Autofac. Searching for comparables, I found https://stackoverflow.com/questions/26358287/how-do-i-resolve-web-api-controllers-using-autofac-in-a-mixed-web-api-and-mvc-ap. From what I can tell the biggest keys to this post are:

  • Make sure to register the api controllers: builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); which you look to be doing, maybe need to confirm it's the correct assembly.
  • Create your own CustomAutofacWebApiDependencyResolver that implements the IDependencyResolver interface.
  • Bind your new resolver (and maybe the MVC resolver as well)

Like so:

//MVC DependencyResolver
DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 

//WebApi DependencyResolver
GlobalConfiguration.Configuration.DependencyResolver = 
    new CustomAutofacWebApiDependencyResolver((IContainer)container); 

My only other issue getting WebAPI/MVC working inside the constraints of Sitecore (8.0 rev. 151127 to be exact) was getting it to recognize my controllers (and the associated Attribute Routing) at all. It involved swapping out the DefaultHttpControllerSelector for my own CustomHttpControllerSelector:

config.Services.Replace(typeof(IHttpControllerSelector), 
    new CustomHttpControllerSelector(config, 
    new NamespaceQualifiedUniqueNameGenerator()));

and using this as a template for CustomHttpControllerSelector

Hoping any little bit of this helps.

Edit:

One piece I forgot to add, which is less WebAPI specific and more DI focused, is on App_Start I iterate thru the types in the assembly and bind all interfaces to a concrete type (in my example, binding all Service and Repository classes to their corresponding Interfaces):

var bindings = (from t in serviceAssembly.GetTypes()
                  where t.IsClass //get all classes
                  where (t.Name.EndsWith("Service") || t.Name.EndsWith("Repository"))
                  let interfaces = t.GetInterfaces()
                  from i in interfaces //get interfaces of all classes
                  where (i.Name.EndsWith("Service") || i.Name.EndsWith("Repository"))
                  //Currently only mapping "services" and "repositories", extend as needed
                  select new {
                    ClassType = t,
                    InterfaceType = i
                  }).ToDictionary(b => b.InterfaceType, b => b.ClassType);

foreach (var binding in bindings) {
    //kernel.Bind(binding.Key).To(binding.Value).InRequestScope(); //Ninject
    builder.RegisterType<binding.Key>().As<binding.Value>();
}
  • The this link doesn't work. Also creating our own resolver hasn't resolved this for us. For now we have decided to not use constructor injection and just get the dependency resolver and do .Resolve<ITemplate>(). If you update your answer with this, I will accept yours as you put so much effort in with the above it only seems fitting. – Anicho Oct 28 '16 at 10:19
  • Link has been updated, sorry it hasn't resolved your current issue, hopefully will help someone in the future. I added another snippet, which may or may not be helpful to you: it registers/binds all interfaces with concrete classes on App_Start based on naming (could be easily adjusted to match Class/Interface differently). – vandsh Oct 28 '16 at 13:52
2

There are two typical reasons of such exception - your dependency resolver was overwritten by something (for that case you can try to call the processor a bit later) or configuration does not know about your controllers because auto assembly load did not get you dll (try BuildManager.GetReferencedAssemblies())

1

Your problem has to do with the ordering of your registrations. The IEntityAccess class must be registered BEFORE the ApiController classes are registered with Autofac. Otherwise, it will not resolve the ApiController constructor correctly.

Without the namespaces, I cannot tell you where exactly you should put it, but if you make sure to place your ApiController registration line AFTER the registration that captures the IEntityAccess class, it should then resolve your ApiController constructor.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.