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

时间:2015-10-14 10:04:57

标签: c# .net asp.net-mvc inversion-of-control

问题

我使用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控制器,它依赖于Manager实例(来自域):

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

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

    ...
}

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

我尝试了什么

所以,我得到的错误不是真正的'错误。显而易见的是确保每个新的实现都在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

我深入挖掘并检查了所有依赖项(它们非常庞大),但所有内容都已正确注册和加载。还没有任何循环依赖关系,所以我可以告诉所有事情应该&#39;工作。因为我并没有得出结论,发生了一些没有被抛出的错误。

4 个答案:

答案 0 :(得分:7)

如果错误显示您需要无参数构造函数,那么即使它已为您的ASP.NET MVC应用程序注册,也表明Unity尚未注册WebAPI。如果你的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它可以控制控制器的创建,从而解决依赖关系。

答案 1 :(得分:2)

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

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

所以,像这样:

我有:

old.managers.IMyManager

new.managers.IMyManager

我在映射中所做的是:

container.RegisterType<IMyManager, MyManager>();

其中IMyManager在这种情况下是 new.managers.IMyManager ,应该是。

此依赖项的消费者之一期望命名空间 old.managers IMyManager

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

答案 2 :(得分:0)

不要忘记在您的web.config中

<system.web>
    <compilation targetFramework="4.8" />
    <httpRuntime targetFramework="4.8" />
    <customErrors mode="Off" />
</system.web>

缺少httpRuntime targetFramework =“ 4.8”部分将导致无参数公共构造函数错误

答案 3 :(得分:-4)

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

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();
相关问题