在构造函数中使用带有附加参数的 Ninject 创建实例

Posted

技术标签:

【中文标题】在构造函数中使用带有附加参数的 Ninject 创建实例【英文标题】:Creating an instance using Ninject with additional parameters in the constructor 【发布时间】:2011-01-14 17:08:23 【问题描述】:

我决定开始使用 Ninject 并遇到一个问题。假设我有以下情况。 我有一个IService 接口和两个实现这个接口的类。而且我还有一个类,它有一个获取 IService 的构造函数和一个 int。如何使用 Ninject 创建这个类的实例(我不想硬连线这个 int,我想每次获得实例时都传递它)?

下面是一些说明情况的代码:

interface IService

    void Func();


class StandardService : IService

    public void Func()
    
        Console.WriteLine("Standard");
    


class AlternativeService : IService

    public void Func()
    
        Console.WriteLine("Alternative");
    



class MyClass

    public MyClass(IService service, int i)
    
        this.service = service;
    

    public void Func()
    
        service.Func();
    

    IService service = null;

class Program

    static void Main(string[] args)
    
        IKernel kernel = new StandardKernel(new InlineModule(
            x => x.Bind<IService>().To<AlternativeService>(),
            x => x.Bind<MyClass>().ToSelf()));

        IService service = kernel.Get<IService>();

        MyClass m = kernel.Get<MyClass>();
        m.Func();
    

【问题讨论】:

【参考方案1】:

为此目的,With.ConstructorArgument 存在于 1.0 中。在 2.0 中,语法略有变化:- With.Parameters.ConstructorArgument with ninject 2.0

有关如何使用上下文、提供程序和参数更正确地传递此类内容的更多详细信息和示例,请参阅Inject value into injected dependency。

编辑:由于 Steven 选择假装我的评论无关紧要,我最好通过一些示例(对于 2.0)阐明我在说什么:

MyClass m = kernel.Get<MyClass>( new ConstructorArgument( "i", 2) );

这在我看来非常清楚,并且准确地说明了正在发生的事情。

如果您可以以更全局的方式确定参数,则可以注册提供程序并这样做:

class MyClassProvider : SimpleProvider<MyClass>

    protected override MyClass CreateInstance( IContext context )
    
        return new MyClass( context.Kernel.Get<IService>(), CalculateINow() );
    

并像这样注册它:

x => x.Bind<MyClass>().ToProvider( new MyClassProvider() )

注意CalculateINow() 位是您在第一个答案中放入逻辑的位置。

或者像这样让它更复杂:

class MyClassProviderCustom : SimpleProvider<MyClass>

    readonly Func<int> _calculateINow;
    public MyClassProviderCustom( Func<int> calculateINow )
    
        _calculateINow = calculateINow;
    

    protected override MyClass CreateInstance( IContext context )
    
        return new MyClass( context.Kernel.Get<IService>(), _calculateINow() );
    

你会这样注册:

x => x.Bind<MyClass>().ToProvider( new MyClassProviderCustom( (  ) => new Random( ).Next( 9 ) ) )

更新:在 Ninject.Extensions.Factory 扩展中体现了较新的机制,这些机制展示了比上述更少的样板文件的改进模式,请参阅: https://github.com/ninject/ninject.extensions.factory/wiki

如前所述,if you need to pass a different parameter each time and you have multiple levels in the dependency graph, you might need to do something like this。

最后一个考虑因素是,因为您没有指定 Using&lt;Behavior&gt;,它将默认为内核选项中指定/默认的默认值(示例中为 TransientBehavior),这可能会导致事实工厂即时计算i [例如,如果对象被缓存]

现在,澄清 cmets 中被 FUD 和掩盖的其他一些观点。关于使用 DI 需要考虑的一些重要事项,无论是 Ninject 还是其他任何东西:

    尽可能多地通过构造函数注入来完成,因此您不需要使用容器特定的属性和技巧。有一篇很好的博客文章叫做Your IoC Container is Showing。

    尽量减少进入容器并请求内容的代码 - 否则您的代码会耦合到 a) 特定容器(CSL 可以将其最小化) b) 整个项目的布局方式。有很好的博客文章表明 CSL 没有做你认为它做的事情。此一般主题称为Service Location vs Dependency Injection。更新:请参阅 http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx 了解详细而完整的理由。

    尽量减少静态和单例的使用

    不要假设只有一个 [global] 容器,并且只要您需要它就可以像一个很好的全局变量一样要求它。正确使用多个模块和Bind.ToProvider() 为您提供了管理此问题的结构。这样每个单独的子系统都可以独立工作,并且您不会将低级组件绑定到***组件等。

如果有人想填写我所指的博客的链接,我将不胜感激(尽管它们都已经从 SO 上的其他帖子链接,所以所有这些只是 UI 引入的重复目的是避免误导性答案的混淆。)

现在,要是 Joel 能进来并真正让我明白什么是好的语法和/或正确的方法就好了!

更新:虽然从获得的赞成票数量来看,这个答案显然很有用,但我想提出以下建议:

以上感觉有点过时了,老实说,反映了很多不完整的想法,自从阅读Dependency Injection in .net以来几乎感觉很尴尬 - 现在运行并购买它 - 这不仅仅是关于 DI,前半部分是一个完整的处理围绕它的所有架构问题都来自一个花了太多时间在依赖注入标签周围徘徊的人。 去阅读Mark Seemann's top rated posts here on SO right now - 你会从每个人身上学到宝贵的技术

【讨论】:

以上是关于在构造函数中使用带有附加参数的 Ninject 创建实例的主要内容,如果未能解决你的问题,请参考以下文章

MVC5 和 Ninject:无参数构造函数错误

使用 Ninject 2 将参数传递给 WCF ServiceHost 类型

Ninject之旅之七:Ninject依赖注入

使用 Ninject BindHttpFilter 时的范围问题

在 MVC3 应用程序中将 Ninject 与自定义角色提供程序一起使用

如何在现有构造函数中添加附加参数。 C++ ASIO