简单注入器 - 确保控制器在生产环境中具有无参数 public 构造函数
Simple Injector - Make sure that the controller has a parameterless public constructor on production
我遇到了一个奇怪的情况。我正在关注 Onion Architecture
,我的架构是这样的:
1-Core
- Domain Classes
- Repository Interfaces
- Service Interfaces
2-Infrastructure
- Data
- Dependency Injection // Here I am using Simple Injector as dependency injection
- Repository Interfaces Implementation
- Service Interfaces Implementation
3-WebApi
- Web Api Project
4-WebClient
- My AngularJs App
5-Test
- Test Project
依赖注入:
[assembly: PreApplicationStartMethod(typeof(IocConfig), "RegisterDependencies")]
namespace Infrastructure.DependencyResolution
{
public class IocConfig
{
public static void RegisterDependencies()
{
var container = new Container();
container.RegisterWebApiRequest<IRepositoryAsync<Category>, Repository<Category>>();
container.RegisterWebApiRequest<ICategoryService, CategoryService>();
container.RegisterWebApiRequest<IDataContextAsync>(() => new MyContext());
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
}
}
}
Web Api 项目:
public class HomeController : ApiController
{
private readonly ICategoryService _categoryService;
public HomeController(ICategoryService categoryService)
{
_categoryService = categoryService;
}
}
我的 local IIS
一切正常。但是现在我已经将这个应用程序发布到生产服务器,现在它给我以下错误:
{"message":"An error has occurred.","exceptionMessage":"An error
occurred when trying to create a controller of type 'HomeController'.
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.d__1.MoveNext()","innerException":{"message":"An
error has occurred.","exceptionMessage":"Type
'JetAdz.WebApi.Controllers.HomeController' 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)"}}
您没有为 WebApi 注册正确的 DependencyResolver。按照WebApi Dependency Injection page and the Simple Injector WebApi page,应该是这样的
public static void Register(HttpConfiguration config)
{
var container = new Container();
container.RegisterWebApiRequest<IRepositoryAsync<Category>, Repository<Category>>();
container.RegisterWebApiRequest<ICategoryService, CategoryService>();
container.RegisterWebApiRequest<IDataContextAsync>(() => new MyContext());
container.Verify();
config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
// Other Web API configuration not shown.
}
您显示的配置用于 MVC 依赖注入(您可能还需要)。
请看一下抛出异常的内部异常。它包含描述发生这种情况的原因的详细信息。
这里有多个问题。您应该确保在容器中显式注册所有根类型。控制器是根类型,因为它们是直接解析的(没有什么依赖于它们,但它们有依赖性)。如果您不显式注册它们,容器将无法在您调用 Verify()
时检查它们是否可以创建。您可以通过调用以下命令来注册您的控制器:
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
这在 the documentation 中有描述。
执行此操作后,您可能会在启动应用程序时直接看到 Verify()
方法失败,在这种情况下,您会收到一条非常详细的消息,解释出了什么问题。
从内部异常可以看出 Web API 的 DefaultHttpControllerActivator.GetInstanceOrActivator
方法正在调用 TypeActivator.Create<T>(Type)
。这仅在调用 request.GetDependencyScope().GetService(controllerType)
returns null
时发生。但是,如果您将 SimpleInjectorWebApiDependencyResolver
连接到 Web API 管道,这通常不会发生。这是因为当请求解析 IHttpController
实现时,SimpleInjectorWebApiDependencyResolver
永远不会 return null
。相反,它会在无法解析控制器时抛出异常。
所以这对我来说表明您的应用程序中存在绑定重定向问题。这意味着 SimpleInjector.Integration.WebApi.dll 指的是与您的应用程序所使用的不同的 IHttpController
版本。绑定重定向旨在解决这个问题,因此请确保您的应用程序具有正确的绑定。对于 System.Web.Http.dll,它看起来像这样(但请注意,您可能还需要其他绑定):
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
...
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
通常绑定由 NuGet 包管理器管理,但它不会定期这样做。
修复绑定重定向问题后,您会看到内部异常包含来自 Simple Injector 的信息,与您在调用 Verify()
.
时看到的信息相同
我使用 spring XML 文件将变量注入我的 classes。在我的例子中,当我创建一个新控制器时,这是一个 copy/paste 错误。
因此,虽然这与 OP 问题并不完全相关,但它会收到相同的错误消息,我帮助了其他人。
我复制了(尖括号改为方括号):
[object id="className" type="Project.ClassName" scope="request"]
[constructor-arg name="OtherClassName" ref="otherclassName"/]
[/object]
当我需要的是:
[object id="className" type="Project.ClassName" scope="request"]
[property name="OtherClassName" ref="otherclassName"/]
[/object]
注意 属性 而不是 constructor-arg.
因为我的 class 没有寻找参数的构造函数。
对我来说,只是我忘了将 SimpleInjectorInitializer.Initialize();
添加到我的 Application_Start()
。
这些天我通常会在 App_Start
文件夹中创建一个 SimpleInjectorInitializer
class,我会在其中执行 container.Verify()
和 GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
调用;在我调用我的 GetInitializeContainer()
方法进行所有类型注册等后
我遇到了一个奇怪的情况。我正在关注 Onion Architecture
,我的架构是这样的:
1-Core
- Domain Classes
- Repository Interfaces
- Service Interfaces
2-Infrastructure
- Data
- Dependency Injection // Here I am using Simple Injector as dependency injection
- Repository Interfaces Implementation
- Service Interfaces Implementation
3-WebApi
- Web Api Project
4-WebClient
- My AngularJs App
5-Test
- Test Project
依赖注入:
[assembly: PreApplicationStartMethod(typeof(IocConfig), "RegisterDependencies")]
namespace Infrastructure.DependencyResolution
{
public class IocConfig
{
public static void RegisterDependencies()
{
var container = new Container();
container.RegisterWebApiRequest<IRepositoryAsync<Category>, Repository<Category>>();
container.RegisterWebApiRequest<ICategoryService, CategoryService>();
container.RegisterWebApiRequest<IDataContextAsync>(() => new MyContext());
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
}
}
}
Web Api 项目:
public class HomeController : ApiController
{
private readonly ICategoryService _categoryService;
public HomeController(ICategoryService categoryService)
{
_categoryService = categoryService;
}
}
我的 local IIS
一切正常。但是现在我已经将这个应用程序发布到生产服务器,现在它给我以下错误:
{"message":"An error has occurred.","exceptionMessage":"An error occurred when trying to create a controller of type 'HomeController'. 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.d__1.MoveNext()","innerException":{"message":"An error has occurred.","exceptionMessage":"Type 'JetAdz.WebApi.Controllers.HomeController' 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)"}}
您没有为 WebApi 注册正确的 DependencyResolver。按照WebApi Dependency Injection page and the Simple Injector WebApi page,应该是这样的
public static void Register(HttpConfiguration config)
{
var container = new Container();
container.RegisterWebApiRequest<IRepositoryAsync<Category>, Repository<Category>>();
container.RegisterWebApiRequest<ICategoryService, CategoryService>();
container.RegisterWebApiRequest<IDataContextAsync>(() => new MyContext());
container.Verify();
config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
// Other Web API configuration not shown.
}
您显示的配置用于 MVC 依赖注入(您可能还需要)。
请看一下抛出异常的内部异常。它包含描述发生这种情况的原因的详细信息。
这里有多个问题。您应该确保在容器中显式注册所有根类型。控制器是根类型,因为它们是直接解析的(没有什么依赖于它们,但它们有依赖性)。如果您不显式注册它们,容器将无法在您调用 Verify()
时检查它们是否可以创建。您可以通过调用以下命令来注册您的控制器:
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
这在 the documentation 中有描述。
执行此操作后,您可能会在启动应用程序时直接看到 Verify()
方法失败,在这种情况下,您会收到一条非常详细的消息,解释出了什么问题。
从内部异常可以看出 Web API 的 DefaultHttpControllerActivator.GetInstanceOrActivator
方法正在调用 TypeActivator.Create<T>(Type)
。这仅在调用 request.GetDependencyScope().GetService(controllerType)
returns null
时发生。但是,如果您将 SimpleInjectorWebApiDependencyResolver
连接到 Web API 管道,这通常不会发生。这是因为当请求解析 IHttpController
实现时,SimpleInjectorWebApiDependencyResolver
永远不会 return null
。相反,它会在无法解析控制器时抛出异常。
所以这对我来说表明您的应用程序中存在绑定重定向问题。这意味着 SimpleInjector.Integration.WebApi.dll 指的是与您的应用程序所使用的不同的 IHttpController
版本。绑定重定向旨在解决这个问题,因此请确保您的应用程序具有正确的绑定。对于 System.Web.Http.dll,它看起来像这样(但请注意,您可能还需要其他绑定):
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
...
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
通常绑定由 NuGet 包管理器管理,但它不会定期这样做。
修复绑定重定向问题后,您会看到内部异常包含来自 Simple Injector 的信息,与您在调用 Verify()
.
我使用 spring XML 文件将变量注入我的 classes。在我的例子中,当我创建一个新控制器时,这是一个 copy/paste 错误。
因此,虽然这与 OP 问题并不完全相关,但它会收到相同的错误消息,我帮助了其他人。
我复制了(尖括号改为方括号):
[object id="className" type="Project.ClassName" scope="request"]
[constructor-arg name="OtherClassName" ref="otherclassName"/]
[/object]
当我需要的是:
[object id="className" type="Project.ClassName" scope="request"]
[property name="OtherClassName" ref="otherclassName"/]
[/object]
注意 属性 而不是 constructor-arg.
因为我的 class 没有寻找参数的构造函数。
对我来说,只是我忘了将 SimpleInjectorInitializer.Initialize();
添加到我的 Application_Start()
。
这些天我通常会在 App_Start
文件夹中创建一个 SimpleInjectorInitializer
class,我会在其中执行 container.Verify()
和 GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
调用;在我调用我的 GetInitializeContainer()
方法进行所有类型注册等后