user1031034
user1031034

Reputation: 856

Autofac DI error in WebApi2 with OWIN

I have problem with DI in WebApi. I have add autofac accordingly with the instruction. But something is wrong with resolving controller.

I get an error:

{
"Message": "An error has occurred.",
"ExceptionMessage": "An error occurred when trying to create a controller of type 'ValuesController'. 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 'WebApiOwinAutoFac.Controllers.ValuesController' 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)"
   }
}

Startup.cs

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var builder = new ContainerBuilder();

        // STANDARD WEB API SETUP:

        // Get your HttpConfiguration. In OWIN, you'll create one
        // rather than using GlobalConfiguration.
        var config = new HttpConfiguration();

        // Register your Web API controllers.
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        // builder.RegisterType<ApplicationDbContext>().AsSelf().InstancePerLifetimeScope();
        builder.RegisterType<TestRepository>().As<ITestRepository>();

        // Run other optional steps, like registering filters,
        // per-controller-type services, etc., then set the dependency resolver
        // to be Autofac.
        var container = builder.Build();
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        // OWIN WEB API SETUP:

        // Register the Autofac middleware FIRST, then the Autofac Web API middleware,
        // and finally the standard Web API middleware.
        app.UseAutofacMiddleware(container);
        app.UseAutofacWebApi(config);
        app.UseWebApi(config);

        ConfigureAuth(app);
    }
}

ValuesController.cs

[RoutePrefix("api/Values")]
public class ValuesController : ApiController
{
    private ITestRepository _context { get; set; }

    public ValuesController(ITestRepository context)
    {
        _context = context;
    }

    // GET api/values/5
    [HttpGet]
    public string GetTest()
    {
        var testList = _context.GetAll();
        return String.Join(",", testList.Select(s => s.Name));
    }
}

ITestRepository.cs

public interface ITestRepository
{
    IEnumerable<TestModel> GetAll();
}

TestRepository.cs

public class TestRepository : ITestRepository
{
    private ApplicationDbContext _context = new ApplicationDbContext();

    public IEnumerable<TestModel> GetAll()
    {
        return _context.Tests;
    }
}

My sample project where I try to make it works, is available on GitHub: Thank you for any help.

Upvotes: 0

Views: 1069

Answers (3)

Win
Win

Reputation: 62300

Thanks to Travis Illig. I also learn new thing today.

You need two things - remove GlobalConfiguration inside Global.ascx.cs, and register Web API route using OWIN.

[assembly: OwinStartup(typeof(WebApiOwinAutoFac.Startup))]
namespace WebApiOwinAutoFac
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var builder = new ContainerBuilder();

            // STANDARD WEB API SETUP:

            // Get your HttpConfiguration. In OWIN, you'll create one
            // rather than using GlobalConfiguration.
            var config = new HttpConfiguration();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            // Register your Web API controllers.
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            // builder.RegisterType<ApplicationDbContext>().AsSelf().InstancePerLifetimeScope();
            builder.RegisterType<TestRepository>().As<ITestRepository>();

            // Run other optional steps, like registering filters,
            // per-controller-type services, etc., then set the dependency resolver
            // to be Autofac.
            var container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            // OWIN WEB API SETUP:

            // Register the Autofac middleware FIRST, then the Autofac Web API middleware,
            // and finally the standard Web API middleware.
            app.UseAutofacMiddleware(container);
            app.UseAutofacWebApi(config);
            app.UseWebApi(config);

            ConfigureAuth(app);
        }
    }
}

Global.ascx.cs

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        //GlobalConfiguration.Configure(WebApiConfig.Register); <-- Delete this line
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

Upvotes: 2

Travis Illig
Travis Illig

Reputation: 23934

The problem is that your sample project isn't using OWIN. It's still using classic non-OWIN ASP.NET Web API. If you look at your Global.asax.cs you'll see that you're still using GlobalConfiguration. You can't use GlobalConfiguration when using OWIN.

You will notice in the Autofac docs on Web API and OWIN integration the following:

A common error in OWIN integration is use of the GlobalConfiguration.Configuration. In OWIN you create the configuration from scratch. You should not reference GlobalConfiguration.Configuration anywhere when using the OWIN integration.

Remove the usage of GlobalConfiguration from your Global.asax and switch entirely to OWIN. You can't mix and match or you will run into trouble like this.

Upvotes: 2

MichaelDotKnox
MichaelDotKnox

Reputation: 1310

You need to tell WebApi how to create your controllers. I like to do it by creating a composition root. Mark Seemann has an excellent article on it here. My implementation looks like this. I am using StructureMap, but AutoFac will have a similar setup. In the constructor, I am passing on the IoC container and in the Create method I get the instance of the controller out of the container using the controllerType parameter.

public class CompositionRoot : IHttpControllerActivator
{
    private readonly IContainer _container;

    public CompositionRoot(IContainer container)
    {
        if (container == null) throw new ArgumentNullException(nameof(container));
        _container = container;
    }

    public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
    {
        var controller = (IHttpController) _container.GetInstance(controllerType);

        return controller;
    }

You wire it up in your startup.cs like this (again, using StructureMap):

config.Services.Replace(typeof (IHttpControllerActivator), new CompositionRoot(container));

This is a more manual wiring up, and it means that you probably won't need these lines:

app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);

Hope that helps

Upvotes: 0

Related Questions