简体   繁体   English

使用OWIN的WebApi2中的Autofac DI错误

[英]Autofac DI error in WebApi2 with OWIN

I have problem with DI in WebApi. 我在WebApi中遇到DI问题。 I have add autofac accordingly with the instruction . 我已经根据指令添加了autofac。 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 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 ITestRepository.cs

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

TestRepository.cs 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. 我尝试使其工作的示例项目可在GitHub找到 :谢谢您的帮助。

Thanks to Travis Illig. 感谢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. 您需要做两件事-删除Global.ascx.cs中的GlobalConfiguration,然后使用OWIN注册Web API路由。

[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 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);
    }
}

The problem is that your sample project isn't using OWIN. 问题在于您的示例项目未使用OWIN。 It's still using classic non-OWIN ASP.NET Web API. 它仍然使用经典的非OWIN ASP.NET Web API。 If you look at your Global.asax.cs you'll see that you're still using GlobalConfiguration . 如果查看一下Global.asax.cs,您会发现您仍在使用GlobalConfiguration You can't use GlobalConfiguration when using OWIN. 使用OWIN时不能使用GlobalConfiguration

You will notice in the Autofac docs on Web API and OWIN integration the following: 您将在有关Web API和OWIN集成Autofac文档中注意到以下内容:

A common error in OWIN integration is use of the GlobalConfiguration.Configuration . OWIN集成中的一个常见错误是使用GlobalConfiguration.Configuration In OWIN you create the configuration from scratch. 在OWIN中,您可以从头开始创建配置。 You should not reference GlobalConfiguration.Configuration anywhere when using the OWIN integration. 使用OWIN集成时,不应在任何地方引用GlobalConfiguration.Configuration

Remove the usage of GlobalConfiguration from your Global.asax and switch entirely to OWIN. 从Global.asax中删除GlobalConfiguration的用法,然后完全切换到OWIN。 You can't mix and match or you will run into trouble like this. 您不能混搭,否则会遇到这样的麻烦。

You need to tell WebApi how to create your controllers. 您需要告诉WebApi如何创建控制器。 I like to do it by creating a composition root. 我喜欢通过创建合成根来做到这一点。 Mark Seemann has an excellent article on it here . 马克西曼(Mark Seemann)在这里有一篇很棒的文章。 My implementation looks like this. 我的实现看起来像这样。 I am using StructureMap, but AutoFac will have a similar setup. 我正在使用StructureMap,但是AutoFac将具有类似的设置。 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. 在构造函数中,我传递了IoC容器,在Create方法中,我使用controllerType参数从容器中获取了控制器的实例。

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): 您可以像下面这样在startup.cs中进行连接(再次使用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 希望能有所帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM