确保控制器具有无参数的公共构造函数错误。

129

我按照这个教程进行操作,一切都很好,直到我修改了我的DbContext,添加了一个额外的构造函数。现在我遇到了解析问题,不确定该怎么办来修复它。是否有一种简单的方法来强制使用无参构造函数,或者我是以错误的方式进行的?

DbContext有两个构造函数:

public class DashboardDbContext : DbContext
{
    public DashboardDbContext() : base("DefaultConnection") { }

    public DashboardDbContext(DbConnection dbConnection, bool owns)
        : base(dbConnection, owns) { }
}

SiteController 构造函数:

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}

存储库:

DashboardDbContext _context;

public DashboardRepository(DashboardDbContext context)
{
    _context = context;
}

UnityResolver 代码:

public class UnityResolver : IDependencyResolver
{
    private readonly IUnityContainer _container;

    public UnityResolver(IUnityContainer container)
    {
        _container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return _container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return _container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = _container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public void Dispose()
    {
        _container.Dispose();
    }
}

WebApi配置文件:

var container = new UnityContainer();
container.RegisterType<IDashboardRepository, DashboardRepository>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);

WebApi调用出现错误:

System.InvalidOperationException:尝试创建类型为“SiteController”的控制器时发生错误。请确保该控制器具有无参数的公共构造函数。

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.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) 
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__0.MoveNext()

InnerException: System.ArgumentException: 类型 'Dashboard.Web.Controllers.SiteController' 没有默认构造函数。

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
错误提示您需要在 SiteController 中添加一个无参数的构造函数,而不是在 DashboardDbContext 中。 - Neil Smith
嗨Smith.h.Neil,但只有在将额外的构造函数添加到dbcontext时才会抛出该错误。如果我删除或注释掉它(第二个构造函数),它就能正常工作。 - scarpacci
我能看到 SiteController 的构造函数吗? - Neil Smith
我猜你是将 DbContext 注入到仓储中了? - Neil Smith
假设您正在使用DI模式,我可以请您在此处指定映射吗?nInject / Unity代码? - codebased
显示剩余8条评论
11个回答

145

发生的问题是您遇到了这个问题。基本上,发生的情况是您没有在容器中明确注册您的控制器。Unity尝试为您解析未注册的具体类型,但由于无法解析(由配置错误引起),它返回null。强制返回null是因为Web API强制执行此操作,因为需要满足IDependencyResolver契约。由于Unity返回null,Web API将尝试自行创建控制器,但由于它没有默认构造函数,它会抛出“确保控制器具有无参数的公共构造函数”异常。此异常消息具有误导性,无法解释真正的原因。

如果您明确注册了控制器,则会看到一个更清晰的异常消息,这就是为什么您应始终明确注册所有根类型的原因。

但是,配置错误当然来自您向DbContext添加第二个构造函数。Unity始终尝试选择具有最多参数的构造函数,但它不知道如何解析此特定构造函数。

因此,真正的原因是您正在尝试使用Unity的自动连接功能来创建DbContextDbContext是一种特殊类型,不应该被自动连接。它是一个框架类型,因此您应该使用工厂委托进行注册

container.Register<DashboardDbContext>(
    new InjectionFactory(c => new DashboardDbContext())); 

请注意,当您重新构建项目时,可能会重置您的登录凭据...在尝试应用此解决方案之前,请执行以下操作:重新构建您的项目,注销自己,然后再次登录,仅在此之后 - 刷新页面并观察问题是否仍然存在。 - ymz
谢谢 - 我后端团队上的这些人总是在Unity配置中违反很多规则。我开始怀疑这是否是每个团队使用IOC容器的方式。 - Dagrooms
1
@Dagrooms:许多开发人员都会被这个问题困扰,但并非所有 DI 容器都存在此问题。例如,Simple Injector 将始终确保在发生此类情况时提示表达式错误。另一个好的提示是:不要使用自定义的 IDependencyResolver,而只使用自定义的 IControllerActivator - Steven

57

在我的情况下,这是因为我注入的依赖项的构造函数内发生了异常(在您的示例中 - 在DashboardRepository构造函数内)。异常被MVC基础结构的某个地方捕获。在相关位置添加日志后,我发现了这一点。


10
这是一个非常重要的回答。当看到“确保控制器具有无参公共构造函数”时,很容易陷入追逐Unity的设置问题的陷阱中,但很可能依赖项已经设置好了,但深藏在内部的异常停止了它的解析。 - Phil Cooper
2
这个!一百万次!我忘记在我的Ninject配置中添加依赖映射。 - Tarps
我的深层异常是属性类型为“string”,而应该是“DateTime?”。如果我没有看到这个答案,就不会去寻找它。非常感谢。 - Jazzy
更像是LousyErrorMessageException()。 - Simon_Weaver
你在哪些相关的地方放置了日志?我使用调试器运行但没有异常,即使我检查了所有CLR异常。我不得不添加手动构造函数解析才得到错误信息。它告诉我要添加诊断以获得可用的错误,最终给了我一些可以使用的东西。 - Arjan

7

我曾经遇到过同样的问题,通过修改UnityConfig.cs文件来解决了它。为了解决UnityConfig.cs文件中的依赖问题,您需要添加以下内容:

public static void RegisterComponents()    
{
    var container = new UnityContainer();
    container.RegisterType<ITestService, TestService>();
    DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}

6

我曾遇到同样的问题。我在Google上搜索了两天,最终无意中发现问题出在控制器的构造函数访问修饰符上。 我没有在控制器的构造函数后面加上public关键字。

public class MyController : ApiController
    {
        private readonly IMyClass _myClass;

        public MyController(IMyClass myClass)
        {
            _myClass = myClass;
        }
    }

我将这次经历作为另一个答案添加,或许其他人也会犯类似的错误。


请确保检查任何注入的依赖项的访问修饰符。如果它们不是public,您将会收到此错误提示。 - Mike D.

4
有时因为你在 ContainerBootstraper.cs 中解析接口,很难捕获错误。在我的情况下,我注入到 api 控制器的接口实现解析出错了。我找不到错误是因为我在 bootstraperContainer 中像这样解析接口: container.RegisterType<IInterfaceApi, MyInterfaceImplementaionHelper>(new ContainerControlledLifetimeManager());
然后我在我的引导容器中添加了以下行: container.RegisterType<MyController>(); 所以当我编译项目时,编译器在上述行中投诉并停止,并显示错误提示。

2
如果你使用 UnityConfig.cs 来注册你的类型映射,像下面这样。最初的回答。
public static void RegisterTypes(IUnityContainer container)
    {
     container.RegisterType<IProductRepository, ProductRepository>();
    }

你需要让 **webApiConfig.cs** 知道有一个容器。最初的回答中包含这一信息。
config.DependencyResolver = new Unity.AspNet.WebApi.UnityDependencyResolver(UnityConfig.Container);

1
在我的情况下,Unity被证明是一个误导。我的问题是由于不同的项目针对不同版本的.NET而导致的。Unity设置正确,一切都正确地注册到容器中。一切都编译得很好。但是类型在类库中,而类库设置为针对.NET Framework 4.0。使用Unity的WebApi项目设置为针对.NET Framework 4.5。将类库也更改为针对4.5解决了我的问题。
我通过注释DI构造函数并添加默认构造函数来发现这一点。我注释掉了控制器方法,并让它们抛出NotImplementedException。我确认我可以访问控制器,并且看到我的NotImplementedException告诉我它正在正确实例化控制器。接下来,在默认构造函数中,我手动实例化了依赖链,而不是依赖于Unity。它仍然编译得很好,但当我运行它时,错误消息又回来了。这为我确认了即使Unity不在场,我仍然会得到错误。最后,我从链的底部开始,逐行注释并重新测试,直到不再收到错误消息为止。这指向了有问题的类的方向,从那里我发现它只隔离在单个程序集中。

1
我真心希望这个答案能帮助其他人节省一天半的时间,不用再浪费时间在Ninject,MVC设计模式,Global.asax和Web Common文件等方面的研究上。
在我的情况下,错误信息本身是完全误导性的。
我的整个应用程序都在正常工作,只有当我调用一个特定的控制器(TestController)时出现了异常。
TestController正在使用Ninject来注入一个接口(ITest),像这样-
public class TestController : ApiController
{
    private readonly ITest _test;

    public TestController (ITest test)
    {
        _test= test;
    }

我正在对TestController中的一个方法进行简单的GET请求,但是在这个线程的问题中,我遇到了上述错误。
最终,我将问题归结为只有在将ITest注入为参数时才会出现错误(因为我测试了另一个接口,它运行良好!)
这让我检查了Test类,并意识到我已经将其自身的实例注入进去了!就像这样 -
public class Test: ITest
{
    private readonly ITest_test;

    public Test(ITest test)
    {
        _test = test;
    }

因此导致整个调用崩溃,作为未处理的异常返回一个完全奇怪的错误,这并没有帮助我!

如果您错误地添加了相同的接口两次,这种情况也会发生。 - Juliyanage Silva

0

安装 Nuget 包 Unit.WebAP 而不是 Unity.MVC5,确保使用 Nuget 安装了正确的 Unity 包。

我安装了 Unity.MVC5,遇到类似的“无参构造函数”异常。

 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>();
    container.RegisterType<ICar, Tesla>();
    GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}

0

如果您的控制器中有一个接口

public myController(IXInterface Xinstance){}

你必须将它们注册到依赖注入容器中。

container.Bind<IXInterface>().To<XClass>().InRequestScope();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接