Ninject在使用隐式自绑定时选择无参数构造函数

时间:2014-03-13 16:17:56

标签: dependency-injection ninject

我在.NET WPF应用程序的MVVM类型场景中使用Ninject版本3。在一个特定的实例中,我使用一个类作为视图和它的视图模型之间的协调器,这意味着首先创建协调器类,并将视图和视图模型(以及其他所需的服务)注入其中。

我有服务的绑定,但我没有为视图/视图模型类创建显式绑定,而是依赖于Ninject的隐式自绑定,因为它们是具体类型而不是接口。

控制台应用程序中此方案的概念版本如下所示:

class Program
{
    static void Main(string[] args)
    {
        StandardKernel kernel = new StandardKernel();

        kernel.Bind<IViewService>().To<ViewService>();
        //kernel.Bind<View>().ToSelf();
        //kernel.Bind<ViewModel>().ToSelf();

        ViewCoordinator viewCoordinator = kernel.Get<ViewCoordinator>();
    }
}

public class View
{

}

public class ViewModel
{

}

public interface IViewService
{

}

public class ViewService : IViewService
{

}

public class ViewCoordinator
{
    public ViewCoordinator()
    {

    }

    public ViewCoordinator(View view, ViewModel viewModel, IViewService viewService)
    {

    }
}

如果你按原样运行这段代码,那么kernel.Get&lt;&gt; call将使用无参数构造函数而不是具有依赖项的构造函数来实例化ViewCoordinator类。但是,如果删除无参数构造函数,Ninject将使用其他构造函数成功实例化该类。这是令人惊讶的,因为Ninject通常会使用构造函数,它可以满足最多的参数。

显然,由于隐式自我绑定,可以满足他们。但如果它没有对其中一个参数进行显式绑定,它似乎首先寻找替代构造函数,它可以使用检查之前查看它是否可以使用隐式自绑定。如果取消注释显式的Bind&lt;&gt;()。ToSelf()行,即使存在无参数构造函数,ViewController类也会正确实例化。

我真的不想为所有视图添加显式自我绑定并查看可能需要它的模型(尽管我知道通过使用基于约定的注册可以减轻负担)。这种行为是设计的吗?在检查其他可用的构造函数之前,有没有办法告诉Ninject检查隐式自绑定?

更新

基于cvbarros&#39;回答我能够通过自己实现IConstructorScorer来实现这一点。以下是我对现有代码所做的更改,以使其发挥作用:

using Ninject.Selection.Heuristics;

class Program
{
    static void Main(string[] args)
    {
        StandardKernel kernel = new StandardKernel();

        kernel.Components.RemoveAll<IConstructorScorer>();
        kernel.Components.Add<IConstructorScorer, MyConstructorScorer>();

        kernel.Bind<IViewService>().To<ViewService>();

        ViewCoordinator viewCoordinator = kernel.Get<ViewCoordinator>();
    }
}


using System.Collections;
using System.Linq;
using Ninject.Activation;
using Ninject.Planning.Targets;
using Ninject.Selection.Heuristics;

public class MyConstructorScorer : StandardConstructorScorer
{
    protected override bool BindingExists(IContext context, ITarget target)
    {
        bool bindingExists = base.BindingExists(context, target);

        if (!(bindingExists))
        {
            Type targetType = this.GetTargetType(target);

            bindingExists = (
                !targetType.IsInterface
                && !targetType.IsAbstract
                && !targetType.IsValueType
                && targetType != typeof(string)
                && !targetType.ContainsGenericParameters
                );
        }

        return bindingExists;
    }

    private Type GetTargetType(ITarget target)
    {
        var targetType = target.Type;
        if (targetType.IsArray)
        {
            targetType = targetType.GetElementType();
        }

        if (targetType.IsGenericType && targetType.GetInterfaces().Any(type => type == typeof(IEnumerable)))
        {
            targetType = targetType.GetGenericArguments()[0];
        }

        return targetType;
    }

}

新的记分员只是通过覆盖BindingExists方法来查看BindingExists调用是否失败,如果是这样,它会检查该类型是否可隐式自绑定。如果是,则返回true,表示Ninject存在该类型的有效绑定。

进行此检查的代码是从Ninject源代码中的SelfBindingResolver类复制的。 GetTargetType代码必须从StandardConstructorScorer复制,因为它在那里声明为private而不是protected。

我的申请现在正常运作,到目前为止,我还没有看到任何负面影响。虽然如果有人知道任何问题,这可能会引起我的进一步投入。

1 个答案:

答案 0 :(得分:2)

默认情况下,Ninject将使用具有大多数绑定的构造函数,当且仅当这些绑定被定义时(在您的情况下它们是隐式的)。在选择要使用的构造函数时,自绑定类型不会加权。

您可以通过对其应用[Inject]属性来标记要使用的构造函数,这将确保选择构造函数。

如果您不想要,可以查看StandardConstructorScorer以查看是否符合您的需求。如果没有,you can replace内核的IConstructorScorer组件与您自己的实现。

相关问题