简单注入器 - 确保控制器在生产环境中具有无参数 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() 方法进行所有类型注册等后