MVC - IoC Unity - 确保控制器具有无参数 public 构造函数

MVC - IoC Unity - Make sure that the controller has a parameterless public constructor

问题

我将 Unity 用作 IoC 并且工作正常,但是随着我不断添加功能,查明任何错误变得越来越困难,因为 Unity 提供的错误是错误的症状,而不是实际错误:

"Message":"An error has occurred.","ExceptionMessage":"An error occurred
when trying to create a controller of type 'MyController'. Make sure that the
controller has a parameterless public
constructor.","ExceptionType":"System.InvalidOperationException"

背景

我有一个 MVC Web Api 控制器,它依赖于管理器实例(来自域):

public class MyController : ApiController
{
    private IMyManager MyManager { get; set; }

    public MyController(IMyManager myManager)
    {
        this.MyManager = myManager;
    }

    ...
}

出现上述错误是因为IMyManager的IoC映射失败。因为它失败了,我没有参数,这意味着 MyController 是使用无参数构造函数调用的,但是由于它不存在(也不应该存在),所以我得到了上述错误。

我试过的

所以,我得到的错误不是 'real' 错误。显而易见的是要确保每个新的实现都在 IoC 下注册,我检查过它们是。我手动执行此操作以使事情易于管理(哦,讽刺!)。

我是这样做的:

container.RegisterType<IMyManager, MyManager>();

但是,这不是问题。

我做的一个修复是循环依赖。我更改了所有涉及的构造函数和方法以使用 属性 值,而不是实例。 我检查了所有相关的 类 并且不再有任何循环依赖。

然而,问题依旧。

问题

我该怎么做才能找出实际问题?手动检查依赖关系的开销太大,因为该结构是一个由更深层次的依赖关系组成的错综复杂的网络。随着应用的成熟,这种情况会变得更糟。

或者,如果 Unity 总是掩盖这些消息(无法修复),是否有其他方法可以提供有价值的错误信息?

更新

根据请求,完整的错误消息(尽管我认为堆栈跟踪不是很有帮助):

{"Message":"An error has occurred.","ExceptionMessage":"An error occurred when trying to create a controller of type 'MyController'. 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)
at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()","InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Type 'MyProject.Web.Api.Controllers.MyController' does not have a default constructor","ExceptionType":"System.ArgumentException","StackTrace":"   at System.Linq.Expressions.Expression.New(Type type)
at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"}}

更新 2

我更深入地研究并检查了所有依赖项(它们非常庞大),但所有内容都已正确注册和加载。也没有任何循环依赖,据我所知,'should' 一切正常。因为它没有,所以我得出结论,发生了一些没有被抛出的错误。

我认为你应该为你的控制器添加另一个构造函数。

public MyController() {}

如果还是报错,你应该检查注册Application_Start的组件(在Global.asax)。在 UnityConfig.cs 中:

public static void RegisterComponents()
    {
        var container = new UnityContainer();

        container.RegisterType<IMyManager, MyManager>();

        MvcUnityContainer.Container = container;

        DependencyResolver.SetResolver(new UnityDependencyResolver(container));
    }

为控制器设置默认值。

public class UnityControllerFactory : DefaultControllerFactory
{
    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        Type controllerType = null;
        if (TypeHelper.LooksLikeTypeName(controllerName))
        {
            controllerType = TypeHelper.GetType(controllerName);
        }

        if (controllerType == null)
        {
            controllerType = this.GetControllerType(requestContext, controllerName);
        }

        return controllerType != null ? this.GetControllerInstance(requestContext, controllerType) : null;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                throw new ArgumentNullException("controllerType");

            if (!typeof(IController).IsAssignableFrom(controllerType))
                throw new ArgumentException(string.Format(
                    "Type requested is not a controller: {0}",
                    controllerType.Name),
                    "controllerType");

            return MvcUnityContainer.Container.Resolve(controllerType) as IController;
        }
        catch
        {
            return null;
        }
    }
}

public static class MvcUnityContainer
{
    public static IUnityContainer Container { get; set; }
}

在Application_Start()中(Global.asax.cs和Global.asax)

UnityConfig.RegisterComponents();

如果错误提示您需要一个无参数的构造函数,这表明 Unity 没有为 WebAPI 注册,即使它已为您的 ASP.NET MVC 应用程序注册。如果你的 IoC 工作正常,它不应该需要一个无参数的构造函数,因为它应该能够解决控制器构造函数中存在的依赖关系。

WebAPI 有自己的 ASP.NET MVC 管道,例如 WebAPI 可以在 OWIN 上托管,因此即使它们存在于同一个项目中,它也需要单独连接。

您应该将 WebAPI 挂接到 UnityConfig 中的依赖项解析器,我相信这里有一个示例:http://www.devtrends.co.uk/blog/using-unity.mvc5-and-unity.webapi-together-in-a-project

using Microsoft.Practices.Unity;
using System.Web.Http;
using System.Web.Mvc;

namespace WebApplication1
{
    public static class UnityConfig
    {
        public static void RegisterComponents()
        {
            var container = new UnityContainer();

            // register all your components with the container here
            // it is NOT necessary to register your controllers

            // e.g. container.RegisterType<ITestService, TestService>();

            // Configures container for ASP.NET MVC
            DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));

            // Configures container for WebAPI
            GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
        }
    }
}

在上面的示例中,有两行用于配置,但它们共享同一个容器,因此共享相同的绑定 - 您不必定义它们两次。如果您曾经想将 WebAPI 项目与 ASP.NET MVC 项目分开,那么您将必须有一个单独的容器和绑定。

就查找问题或 Unity 向您隐藏错误而言,根据我的经验,情况并非如此。您遇到的错误直接来自 ASP.NET MVC。在没有 IoC 的正常使用中,您需要一个无参数的构造函数...使用 IoC,它控制控制器的创建,从而解决依赖关系。

我找到了,问题是我在同一个解决方案中重新制作现有代码,这意味着将有相同名称的重复文件。

在这种特殊情况下,我有一个与以前存在的界面完全相同的界面。在代码深处的某个地方,我使用了错误的命名空间,这意味着 IoC 映射不正确。

所以,像这样:

我有:

old.managers.IMyManager

new.managers.IMyManager

我在映射中所做的是:

container.RegisterType<IMyManager, MyManager>();

在这种情况下 IMyManager 是 new.managers.IMyManager,因为它应该是。

此依赖项的其中一个消费者期望命名空间 old.managers.

IMyManager

更新消费者以使用新命名空间修复了它。

不要在你的 web.config

中忘记这一点
<system.web>
    <compilation targetFramework="4.8" />
    <httpRuntime targetFramework="4.8" />
    <customErrors mode="Off" />
</system.web>

缺少httpRuntime targetFramework="4.8"部分会导致无参public构造函数错误