问题
我使用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;工作。因为我并没有得出结论,发生了一些没有被抛出的错误。
答案 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();