Ninject + MVC3无法注入到控制器中。

14

我使用了NuGet Ninject MVC3扩展,但无法在请求时注入到控制器中。似乎没有绑定,因为MVC正在寻找无参数的构造函数。以下是堆栈跟踪:

    [MissingMethodException: No parameterless constructor defined for this object.]
       System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
       System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +98
       System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +241
       System.Activator.CreateInstance(Type type, Boolean nonPublic) +69
       System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +67

    [InvalidOperationException: An error occurred when trying to create a controller of type 'MyApp.Presentation.Controllers.SearchController'. Make sure that the controller has a parameterless public constructor.]
       System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +182
       System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) +80
       System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName) +74
       System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +199
       System.Web.Mvc.<>c__DisplayClass6.<BeginProcessRequest>b__2() +49
       System.Web.Mvc.<>c__DisplayClassb`1.<ProcessInApplicationTrust>b__a() +13
       System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
       System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
       System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Func`1 func) +124
       System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +98
       System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +50
       System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
       System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8862676
       System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184

我正在尝试注入的控制器(目前使用基类):

public class SearchController : MainBaseController
{

    private readonly MyApp.Domain.Tutor.TutorSearch TutorSearch;
    protected static readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    [Ninject.Inject]
    public SearchController(MyApp.Domain.Tutor.TutorSearch tutorSearch)
    {
        this.TutorSearch = tutorSearch;
    }

    .... (no other constructors)
}

以下是我 NinjectWebCommon.cs 中相关的部分:

    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);
        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        INinjectModule[] modules = new INinjectModule[]
        {
            new Domain.DomainModule()
        };

        kernel.Load(Assembly.GetExecutingAssembly());
        kernel.Load(modules);

        kernel.Bind<MyApp.Domain.Tutor.TutorSearch>().ToSelf();

        // ***************
        var t = kernel.Get<MyApp.Presentation.Controllers.SearchController>();
    }

最后,这是DomainModule:

public class DomainModule : NinjectModule
{
    public override void Load()
    {
        Bind<Data.Profile.IProfileProvider>().To<Data.Profile.XmlProfileProvider>();
        Bind<Data.Subject.ISubjectProvider>().To<Data.Subject.XmlSubjectProvider>();
    }
}

您可以在我的NinjectWebCommon.cs文件中看到星号标记的行,在那里我尝试显式构建控制器进行测试。这个控制器可以完美地工作,正常注入了Xml..Providers。

我错过了什么吗?我对NuGet MVC3扩展中发生的事情不太了解,因此不知道控制器绑定失败的具体原因。

如果您能提供一些指导,请告诉我,非常感谢!


Traw,你使用具体实现而不是接口有什么特别的原因吗?我已经添加了一个快速示意图来展示它应该是什么样子。 - jim tollan
3个回答

18

在经过相当长时间的绝望摆弄之后,我终于找到了我的问题。我引用了MVC4(System.Web.Mvc)DLL,并且没有将旧版本3.0.0.0的绑定重定向到4.0.0.0:

  <dependentAssembly>
    <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
    <bindingRedirect oldVersion="1.0.0.0" newVersion="4.0.0.0" />
    <bindingRedirect oldVersion="2.0.0.0" newVersion="4.0.0.0" />
    <!-- The following line was missing -->
    <bindingRedirect oldVersion="3.0.0.0" newVersion="4.0.0.0" /> 
  </dependentAssembly>

至少,我希望这是一个不错的解决方案。

编辑:如下建议,以下一行更为简洁:

  <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />

这个链接帮助我解决了一个类似的问题。在这里... - melkisadek
7
“bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"”这一行代码不就可以涵盖这三种情况吗? - neontapir
我该在哪里找到这个文件?我遇到了同样的问题。 - Doug
算了,我在另一个项目的 web-config 文件中找到了。由于某种原因,我的配置文件中缺少 dependentAssembly 部分。 - Doug
4
希望在2053年或更早的时候解决这个问题。 - vtortola
显示剩余2条评论

6
使用Visual Studio和Nuget,您需要运行以下命令:
Install-Package Ninject.MVC3
(根据您正在运行的MVC版本替换3)
这将解决您的问题。

这对我来说或多或少有效。我以为我已经安装了Ninject.MVC5(Ninject.Web.Mvc),但实际上并没有。由于某种原因,我只安装了Ninject和Ninject.Web.Common。 - Marnee KG7SIO

1
Traw,你使用具体实现而不是接口有特别的原因吗?就目前而言,你从 DI 直接注入类中没有获得任何好处(我很惊讶你能让它工作),我会重构并将所有内容放在接口后面,然后将接口绑定到具体实现。以下是如何实现的快速示例:
// 'imagined' ISearchType interface
public interface ISearchType
{
    int Id { get; set; }
    string LastName { get; set; }
}

// 'imagined' TutorSearch concrete class
public class TutorSearch : ISearchType
{
    public int Id { get; set; }
    public string LastName { get; set; }
}

// your revised controller code
private readonly ISearchType _tutorSearch;
public SearchController(ISearchType searchType)
{
    _tutorSearch = searchType;
}

然后,在您注册服务(Ninject)时,您需要添加:

kernel.Bind<ISearchType>().To<TutorSearch>();

这应该能让你更进一步。另外,var t = kernel.Get<...> 实际上什么也没做 - 以防你想知道发生了什么。

玩得开心...


嗨,吉姆。首先,感谢您的回复。我使用具体实现的原因是因为在这个阶段我还没有把它分解出来,而且所涉及的类有我想要注入的依赖项。我以为Ninject可以很好地注入具体实现,但事实是否如此呢? 'var t = kernel.Get<...>' 这一行只是我在调试器中查看的一个测试,它可以正常工作 - Ninject完美地构造了控制器。只是在Web请求时没有成功。 我还尝试传递接口,结果也是一样的! - Traw
嗨Traw-很高兴你已经顺利完成了所有工作。然而,我不建议通过ninject注入具体类(即使你可以)。你能得到什么好处??你最好只是在空构造函数中创建类,因为它永远不会改变实现。我的建议是从接口开始编码你的具体实现。这是最佳实践,因为你只需要重构具体类中的代码。此外,对于单元测试,你无法基于提供的代码示例进行任何操作。 - jim tollan
另一个使用接口编码的优点是,您可以将工作分配给其他开发人员,并知道实现将可注入到现有功能中。如果您从这个线程中学到了任何东西,请向我保证,您会重新开始并从接口开始工作 - 拜托了,拜托了,非常拜托了 :) 祝你好运.. - jim tollan
1
感谢您的热情呼吁,Jim。请放心,我确实会在适当的情况下使用接口。但在这种情况下,由于没有可预见的需求,我需要尽快运行一些东西,所以没有使用接口。我在这里注入它纯粹是因为我正在让Ninject注入其依赖项,这些依赖项由较低层中的模块提供。这使得表示层不必了解较低层的依赖关系。 此外,情况并不像看起来那么糟糕。虽然我完全同意您所列出的所有好处 :) - Traw

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