1
votes

I'm self-hosting a WebApi application in order to do some integration tests.

I set up my server like this:

var httpConfig = new HttpSelfHostConfiguration(BaseAddress);

new ApiServiceConfiguration(httpConfig)
    .Configure();

var server = new HttpSelfHostServer(httpConfig);
server.OpenAsync().Wait();
Server = server; //this is just a property on the containing class

ApiServiceConfiguration is a class that allows me to abstract the config of the WebApi (So I can use it within Global.asax for IIS hosted version of the api)

Here's an extract:

public class ApiServiceConfiguration 
   {
       private readonly HttpConfiguration _httpConfiguration;

       public ApiServiceConfiguration(HttpConfiguration httpConfiguration)
       {
           _httpConfiguration = httpConfiguration;
       }

       public void Configure()
       {
           //setup routes
           RouteConfig.RegisterRoutes(_httpConfiguration.Routes);

           //setup autofac
           AutofacConfig.Setup(_httpConfiguration);

My AutofacConfig.Setup static method is just:

public static void Setup(HttpConfiguration config)
{
    var builder = new ContainerBuilder();

    // Register the Web API controllers.
    builder.RegisterApiControllers(Assembly.GetAssembly(typeof(MyController)));

    // Register dependencies.

    // Build the container.
    var container = builder.Build();

    // Configure Web API with the dependency resolver.
    //notice config is passed in as a param in containing method
    config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}

However, when I call my api from my unit test:

using (var client = new HttpClient(Server))
{
    var result = client.PostAsync(BaseAddress + "api/content/whatever"

    var message = result.Content.ReadAsStringAsync().Result;
}

The value of message is:

An error has occurred.","ExceptionMessage":"An error occurred when trying to create a controller of type 'ContentController'. 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) 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.d__0.MoveNext()","InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Type 'My.Namespace.ContentController' does not have a default constructor","ExceptionType":"System.ArgumentException","StackTrace":" at System.Linq.Expressions.Expression.New(Type type) at System.Web.Http.Inteal.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)

Which indicates to me that the Autofac controller registration has not worked.

If I stick a breakpoint on the following line in my set up:

var server = new HttpSelfHostServer(httpConfig);

And use the immediate window to check httpConfig.DependencyResolver it is indeed related to Autofac

2
Can you post the MyController class?Piers Karsenbarg
I could... but it's just a regular controller with a dependency, and it works in a "live" setting (ie hosted in IIS / cassini)Alex
Are you using MVC4 with WebApi1 or MVC5 with WebApi2? Because I cannot repro your issue using your code in a newly created MVC4/WebApi1 application with using a console application as a selfhost... In your clientcode try to log out that the Server.Configuration.DependencyResolver is still the autofac dependency resolver. Otherwise try to repro this in a new project and upload it to somewhere.nemesv

2 Answers

1
votes

You need to move the ContainerBuilder and the DependencyResolver out of the static class. Your implementation is using a late binding and the autofac dependency resolver is not replacing the default resolver.

You can see this in the error message: "Make sure that the controller has a parameterless public constructor."

The default resolver ist not able to handle Controllers without a parameterless constructor, which is required for constructor injection.

0
votes

I had the same problem. In my case, the issue was with using Assembly.GetCallingAssembly(). I wanted to get the calling assembly and pass it to Autofac to register the controllers. But in release mode it didn't work.

After reading the following article:

http://www.ticklishtechs.net/2010/03/04/be-careful-when-using-getcallingassembly-and-always-use-the-release-build-for-testing/

I just removed GetCallingAssembly call and replaced with passing the assembly explicitly, like typeof(Startup).Assembly