C# MVC-IoC Unity-确保控制器具有无参数公共构造函数
问题 我使用Unity作为IoC,这很好,但随着我不断添加功能,查明任何错误变得越来越困难,因为Unity提供的错误是错误的症状,而不是实际错误:C# MVC-IoC Unity-确保控制器具有无参数公共构造函数,c#,.net,asp.net-mvc,inversion-of-control,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 paramete
"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控制器,它依赖于管理器实例(来自域):
发生上述错误的原因是IMyManager的IoC映射失败。当它失败时,我没有参数,这意味着MyController是使用无参数构造函数调用的,但是由于它不存在(也不应该存在),我得到了上面的错误
我尝试过的
所以,我得到的错误不是真正的错误。最明显的是要确保每一项新的实施都在国际奥委会注册,我检查过了,他们都在注册。我用手工操作来保持事情的可控性(真是讽刺!)
我是这样做的:
container.RegisterType<IMyManager, MyManager>();
container.RegisterType();
但是,这不是问题所在
我做的一个修复是循环依赖。我将所有涉及的构造函数和方法都更改为使用属性值,而不是实例。
我检查了所有涉及的类,不再存在任何循环依赖项
然而,问题仍然存在
问题
我能做些什么来找出实际问题?手动检查依赖项的开销太大了,因为该结构是一个由更深层次的依赖项组成的复杂网络。随着应用程序的成熟,情况会变得更糟
或者,如果Unity总是掩盖这些消息(无法修复),那么是否有其他方法可以提供有价值的错误信息
更新
对于每个请求,完整的错误消息(尽管我认为stacktrace没有多大帮助):
{“Message”:“出现错误”,“ExceptionMessage”:“尝试创建“MyController”类型的控制器时出错。请确保该控制器具有无参数公共构造函数。”,“ExceptionType”:“System.InvalidOperationException”,“StackTrace”:“at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.create”(HttpRequestMessage请求,HttpControllerDescriptor controllerDescriptor,类型controllerType)
在System.Web.Http.Controllers.HttpControllerDescriptor.CreateController上(HttpRequestMessage请求)
在System.Web.Http.Dispatcher.HttpControllerDispatcher.d_u1.MoveNext()“,“InnerException:{”消息“:“发生了错误”,“ExceptionMessage:”类型“MyProject.Web.Api.Controllers.MyControllers”没有默认构造函数,“ExceptionType:”System.ArgumentException”,“StackTrace:”在System.Linq.Expressions.Expression.New(类型)
在System.Web.Http.Internal.TypeActivator.Create[TBase](类型instanceType)中
位于System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceActivator(HttpRequestMessage请求,类型controllerType,Func`1&activator)
在System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage请求,HttpControllerDescriptor controllerDescriptor,Type controllerType)“}
更新2
我挖得更深一点,检查了所有依赖项(它们非常庞大),但所有内容都已正确注册和加载。就我所知,也没有任何循环依赖项,所以所有内容都“应该”工作。由于没有,我得出结论,发生了一些错误,没有抛出。我认为您应该为控制器添加另一个构造函数
public MyController() {}
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; }
}
如果仍有错误,则应检查注册应用程序启动的组件(在Global.asax中)。在UnityConfig.cs中:
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IMyManager, MyManager>();
MvcUnityContainer.Container = container;
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
在应用程序中_Start()(Global.asax.cs和Global.asax)
如果错误表明您需要无参数构造函数,则向我表明Unity未注册WebAPI,即使它已注册为您的ASP.NET MVC应用程序。如果您的IoC工作正常,它不应该需要无参数构造函数,因为它应该能够解决存在于您的构造函数中的依赖项特罗勒的构造器 WebAPI有自己通向ASP.NET MVC的管道,例如WebAPI可以在OWIN中托管,因此即使它们存在于同一个项目中,也需要单独连接 您应该将WebAPI挂接到UnityConfig中的依赖项解析程序,我相信这里有一个例子:
使用Microsoft.Practices.Unity;
使用System.Web.Http;
使用System.Web.Mvc;
命名空间WebApplication1
{
公共静态类UnityConfig
{
公共静态无效注册表组件()
{
var container=new UnityContainer();
//在此处向容器注册所有组件
//无需注册控制器
//例如container.RegisterType();
//为ASP.NET MVC配置容器
DependencyResolver.SetResolver(新Unity.Mvc5.UnityDependencyResolver(容器));
//为WebAPI配置容器
GlobalConfiguration.Configuration.DependencyResolver=新的Unity.WebApi.UnityDependencyResolver(容器);
}
}
}
在上面的示例中,有两行用于配置,但它们共享同一个容器,因此具有相同的绑定-您不必定义它们两次。如果要将WebAPI项目与ASP.NET MVC项目分开,则必须有单独的容器和绑定
就发现问题或向您隐藏错误而言,根据我的经验,情况并非如此。您遇到的错误直接来自ASP.NET MVC。在没有IoC的正常使用中,您需要一个无参数构造函数……有了IoC,它可以控制控制器的创建,从而解决依赖关系。我发现了,问题是我正在改造现有的
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; }
}
UnityConfig.RegisterComponents();
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);
}
}
}
old.managers.IMyManager
new.managers.IMyManager
container.RegisterType<IMyManager, MyManager>();
<system.web>
<compilation targetFramework="4.8" />
<httpRuntime targetFramework="4.8" />
<customErrors mode="Off" />
</system.web>