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构造函数错误
问题
我将 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构造函数错误