在kernel.Get<type>时出现了InvalidCastException。

5
我有一个WPF应用程序,可能会使用命令行参数启动。我曾经将我的组合根放在App.xaml代码后台的OnStartup(StartupEventArgs e)方法中,但这会导致应用程序关闭问题,因此我将App.xaml转换为“页面”(而不是“应用程序定义”),并编写了自己的Program类来包含我的应用程序入口点,这将成为我的新组合根位置。
自从那时起,我就无法启动应用程序,Ninject似乎无法解析应用程序的主要对象(或者它可能是其中一个依赖项?)。
这个异常让我浪费了很多时间,堆栈跟踪都是Ninject-内部的,我不知道要在我的代码中修复什么,导致此异常的类型的绑定方式最近没有改变:
at DynamicInjector54d92ac63a2e47fda5ffbcc19b9942a9(Object[] )
at Ninject.Activation.Providers.StandardProvider.Create(IContext context)
at Ninject.Activation.Context.Resolve()
at Ninject.KernelBase.<>c__DisplayClass10.<Resolve>b__c(IBinding binding)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
at Ninject.Planning.Targets.Target`1.GetValue(Type service, IContext parent)
at Ninject.Planning.Targets.Target`1.ResolveWithin(IContext parent)
at Ninject.Activation.Providers.StandardProvider.GetValue(IContext context, ITarget target)
at Ninject.Activation.Providers.StandardProvider.<>c__DisplayClass4.<Create>b__2(ITarget target)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at Ninject.Activation.Providers.StandardProvider.Create(IContext context)
at Ninject.Activation.Context.Resolve()
at Ninject.KernelBase.<>c__DisplayClass10.<Resolve>b__c(IBinding binding)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
at Ninject.Planning.Targets.Target`1.GetValue(Type service, IContext parent)
at Ninject.Planning.Targets.Target`1.ResolveWithin(IContext parent)
at Ninject.Activation.Providers.StandardProvider.GetValue(IContext context, ITarget target)
at Ninject.Activation.Providers.StandardProvider.<>c__DisplayClass4.<Create>b__2(ITarget target)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at Ninject.Activation.Providers.StandardProvider.Create(IContext context)
at Ninject.Activation.Context.Resolve()
at Ninject.KernelBase.<>c__DisplayClass10.<Resolve>b__c(IBinding binding)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
at Ninject.Planning.Targets.Target`1.GetValue(Type service, IContext parent)
at Ninject.Planning.Targets.Target`1.ResolveWithin(IContext parent)
at Ninject.Activation.Providers.StandardProvider.GetValue(IContext context, ITarget target)
at Ninject.Activation.Providers.StandardProvider.<>c__DisplayClass4.<Create>b__2(ITarget target)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at Ninject.Activation.Providers.StandardProvider.Create(IContext context)
at Ninject.Activation.Context.Resolve()
at Ninject.KernelBase.<>c__DisplayClass10.<Resolve>b__c(IBinding binding)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters)
at MyProgram.Program.Main(String[] args) in C:\Dev\MyProject\MyProject.WinPresentation\Program.cs:ligne 40
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

这里是主方法/应用程序入口:

Main 方法:

[STAThread]
public static void Main(string[] args)
{
    var module = new MyAppNinjectModule(args);
    var kernel = new StandardKernel(module);

    var argsHelper = module.CommandLineArgs;

    var logProvider = kernel.Get<ILogProvider>();
    var logger = logProvider.GetLogger(typeof(Program).Name);

    if (argsHelper.LoggingDisabledArgument.IsSpecified()) logProvider.DisableLogging();
    logger.Info(log.LogAppStart);

    var installer = kernel.Get<IInstaller>(); // >>> InvalidCastException here

    if (argsHelper.QuietInterfaceArgument.IsSpecified())
    {
        // running with -quiet command-line switch: just execute and exit.
        installer.Execute();
    }
    else
    {
        // instantiate a new App object (WPF), and run it.
        // installer.Execute() may or may not be executed, depending on user actions.
        var app = new App(installer);
        app.Run();
    }
}

NinjectModule将绑定到某个实现,具体取决于提供的命令行参数(例如,在指定了时,将IInstaller绑定到,在没有指定时,则绑定到等)。通常情况下,Ninject会提供非常有用和详细的异常消息,当出现时生活很美好。但是这个让我无从下手,因为我没有执行无效转换的人,并且我甚至不知道涉及哪些类型。我只知道我可能写了一些Ninject不喜欢的代码,也许与我绑定到它的实现的方式有关,但是如果我注释掉NinjectModule的“分支”部分来强制绑定我要找的特定实现(),它仍然会失败并抛出这个。
实现的构造函数:
public ManualInstaller(IView<MainWindowViewModel> view, 
                       IProcessHelper processHelper,
                       ISettingsHelper settingsHelper,
                       ILogProvider logProvider,
                       ISetupBootstrapper installer,
                       bool notifySuccess)
    : base(notifySuccess, processHelper, settingsHelper, logProvider, installer)

相应的绑定代码(其他依赖项已经绑定,没有ActivationException,所以不确定这与我的问题有多大关联):

var msg = string.Empty;
if (CommandLineArgs.CompletionMessageArgument.IsSpecified()) 
    msg = CommandLineArgs.CompletionMessageArgument.ParameterValue();

Bind<MainWindowViewModel>().ToSelf().WithConstructorArgument("completionMessage", msg);
Bind<IView<MainWindowViewModel>>().To<MainWindow>();

Bind<IInstaller>().To<ManualInstaller>()
                  .WithConstructorArgument("notifySuccess", notifySuccess);

如果需要找出问题所在,不要犹豫让我知道还需要什么信息...

编辑

逐个解决安装程序的依赖项可以提供更多信息:

// resolve installer dependencies:
var view = kernel.Get<IView<MainWindowViewModel>>(); // >>> InvalidCastException here
var processHelper = kernel.Get<IProcessHelper>();
var settingsHelper = kernel.Get<ISettingsHelper>();
var bootstrapper = kernel.Get<ISetupBootstrapper>();
var installer = new ManualInstaller(view, processHelper, settingsHelper, logProvider, bootstrapper, true);

我至少可以缩小问题范围到需要解决的一个特定依赖关系:问题要么出在View上,要么出在其唯一的依赖项——ViewModel上。因此,我这样做:

// resolve ViewModel dependencies:
var processHelper = kernel.Get<IProcessHelper>(); // >>> InvalidCastException here
var settingsHelper = kernel.Get<ISettingsHelper>();
var messenger = kernel.Get<INetworkMessenger>();
var factory = kernel.Get<IBuildServerFactory>();
var dialogs = kernel.Get<ICommonDialogs>();

显然,问题出在 IProcessHelper 实现上 - 再次出现了:

// resolve ProcessHelper dependencies:
var processWrapper = kernel.Get<IProcessWrapper>();
var wmiWrapper = kernel.Get<IWindowsManagementInstrumentationWrapper>();
var helper = new ProcessHelper(processWrapper, wmiWrapper, logProvider, 300);

现在我不再收到InvalidCastException错误。

以下是有问题的类的构造函数和字段:

private readonly ILogProvider _logProvider;
private readonly IProcessWrapper _process;
private readonly IWindowsManagementInstrumentationWrapper _wmi;
public int TimeoutSeconds { get; private set; }

public ProcessHelper(IProcessWrapper process, 
                     IWindowsManagementInstrumentationWrapper wmiWrapper, 
                     ILogProvider logProvider, 
                     int timeout)
{
    _logProvider = logProvider;
    _process = process;
    _wmi = wmiWrapper;
    TimeoutSeconds = timeout;
}

如何使用NinjectModule进行绑定:

Bind<IProcessHelper>().To<ProcessHelper>()
                      .WithConstructorArgument("timeout", Properties.Settings.Default.ProcessTimeoutSeconds);

在@jure的评论帮助下,我发现Properties.Settings.DefaultProcessTimeoutSeconds的类型实际上设置为string - 显然它需要一个int类型的值。正是这个问题!


1
只是猜测,也许您绑定设置中的某些构造函数参数不是预期类型。WithConstructorArgument无法检查传递给它的对象是否是构造函数参数的有效类型。这可能是InvalidCastException的来源。您应该检查“msg”和“notifySuccess”对象是否对MainWindowViewModel和ManualInstaller构造函数有效。 - Jurica Smircic
@jure 真是太准确了(请参见重新编辑),而且你在我发布第一次编辑之前就猜到了... 你真是个明星!将其作为答案发布,我很乐意点赞并接受它! - Mathieu Guindon
很高兴你找到了问题的原因...我也发布了答案。 - Jurica Smircic
我绑定它的方式已经改变了;在重构过程中,我将应用程序设置移动到了“MyApp.Core”程序集中,可能忘记将该设置的默认“String”类型更改为“int”。 - Mathieu Guindon
1个回答

6

由于您向 WithConstructorArguments 设置中传递了常数值,因此您需要确保对象类型对实现类构造函数参数有效。

正如您后来发现的那样,在设置中将 string 对象作为构造函数参数传递,但构造函数预期一个 int 。 这导致您收到了 InvalidCastException 异常。

顺便说一句,如果Ninject提供了更好的异常处理,那就太好了,否则这确实很难调试。


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