使用 OWIN 的 WebApi2 中的 Autofac DI 错误
Autofac DI error in WebApi2 with OWIN
我在 WebApi 中遇到 DI 问题。我已经使用 instruction 相应地添加了 autofac。但是解析控制器有问题。
我收到一个错误:
{
"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;
}
}
我尝试使其工作的示例项目可在 GitHub 上获得:
感谢您的帮助。
感谢 Travis Illig。我今天也学到了新东西。
您需要做两件事 - 删除 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
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);
}
}
您需要告诉 WebApi 如何创建您的控制器。我喜欢通过创建合成根来实现。 Mark Seemann 有一篇关于它的优秀文章 here。我的实现看起来像这样。我使用的是 StructureMap,但 AutoFac 也有类似的设置。在构造函数中,我传递 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;
}
你像这样在你的 startup.cs 中连接它(再次使用 StructureMap):
config.Services.Replace(typeof (IHttpControllerActivator), new CompositionRoot(container));
这是一个更手动的连接,这意味着您可能不需要这些线路:
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
希望对您有所帮助
问题是您的示例项目没有使用 OWIN。它仍在使用经典的非 OWIN ASP.NET Web API。如果您 look at your Global.asax.cs,您会发现您仍在使用 GlobalConfiguration
。使用 OWIN 时不能使用 GlobalConfiguration
。
您会在 the Autofac docs on Web API and OWIN integration 中注意到以下内容:
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.
从您的 Global.asax 中删除 GlobalConfiguration
的用法并完全切换到 OWIN。你不能混搭,否则你会 运行 陷入这样的麻烦。
我在 WebApi 中遇到 DI 问题。我已经使用 instruction 相应地添加了 autofac。但是解析控制器有问题。
我收到一个错误:
{
"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;
}
}
我尝试使其工作的示例项目可在 GitHub 上获得: 感谢您的帮助。
感谢 Travis Illig。我今天也学到了新东西。
您需要做两件事 - 删除 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
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);
}
}
您需要告诉 WebApi 如何创建您的控制器。我喜欢通过创建合成根来实现。 Mark Seemann 有一篇关于它的优秀文章 here。我的实现看起来像这样。我使用的是 StructureMap,但 AutoFac 也有类似的设置。在构造函数中,我传递 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;
}
你像这样在你的 startup.cs 中连接它(再次使用 StructureMap):
config.Services.Replace(typeof (IHttpControllerActivator), new CompositionRoot(container));
这是一个更手动的连接,这意味着您可能不需要这些线路:
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
希望对您有所帮助
问题是您的示例项目没有使用 OWIN。它仍在使用经典的非 OWIN ASP.NET Web API。如果您 look at your Global.asax.cs,您会发现您仍在使用 GlobalConfiguration
。使用 OWIN 时不能使用 GlobalConfiguration
。
您会在 the Autofac docs on Web API and OWIN integration 中注意到以下内容:
A common error in OWIN integration is use of the
GlobalConfiguration.Configuration
. In OWIN you create the configuration from scratch. You should not referenceGlobalConfiguration.Configuration
anywhere when using the OWIN integration.
从您的 Global.asax 中删除 GlobalConfiguration
的用法并完全切换到 OWIN。你不能混搭,否则你会 运行 陷入这样的麻烦。