14

我有一个IConfig对象,其中包含在我的应用程序中使用的设置。此刻,我将整个对象注入到每个需要它的对象的构造函数中,如下:

public interface IConfig 
{
    string Username { get; }
    string Password { get; }
    //... other settings
}

public class Foo : IFoo
{
    private readonly string username;
    private readonly string password;

    public Foo(IConfig config)
    {
        this.username = config.Username;
        this.password = config.Password;
    }
}

缺点是IConfig包含大量设置,因为它是从整体配置文件反序列化的,因此不需要注入整个对象。我想做的是将构造函数更改为,Foo(string username, string password)以便它只接收它需要的设置。这也使得创建Foo用于测试的对象变得更加容易(不必IConfig为了创建而设置Foo)。我想直接在 my 中绑定构造函数参数NinjectModule,如下所示:

public class MyModule : NinjectModule
{
    public override void Load()
    {
        Bind<IConfig>().To<JsonConfig>()
            .InSingletonScope();

        Bind<IFoo>().To<Foo>()
            .WithConstructorArgument("username", IConfig.Username)
            .WithConstructorArgument("password", IConfig.Password);
    }
}

显然这段代码不起作用,但我将如何去做我想做的事?

我最初的想法是使用NinjectModule.Kernel获取IKernel然后获取我的IConfig对象的实例并根据需要注入属性,但是返回的对象NinjectModule.Kernel没有Get<T>()方法。

4

2 回答 2

15

你走在正确的轨道上:

该方法是在名称空间中Kernel.Get<T>()定义的扩展方法,因此添加它也可以在您的模块中使用。ResolutionExtensionsNinjectusing Ninject;

但是,Module.Kernel您应该使用IContext在第二个重载中提供的WithConstructorArgument来获取Kernel

Bind<IFoo>().To<Foo>()
    .WithConstructorArgument("username", 
                             context => context.Kernel.Get<IConfig>().Username)
    .WithConstructorArgument("password", 
                             context => context.Kernel.Get<IConfig>().Password);
于 2012-05-19T07:05:03.337 回答
1

这可能是接口隔离原则的一个很好的候选。

在这种情况下,定义另一个接口,例如ICredentialConfig仅包含UsernameandPassword属性的接口,然后IConfig实现该接口。

public Interface ICredentialConfig
{
   string Username { get; }
   string Password { get; }
}

public Interface IConfig : ICredentialConfig
{
   //... other settings
}

现在 makeFoo依赖于ICredentialConfig而不是IConfig. 然后您可以:

  1. 注入您JsonConfig使用的 Ninject,而不是使用硬编码的参数名称。
  2. 实现/模拟一个ICredentialConfig用于在测试中实例化Foo,而不是必须实现完整的IConfig接口。
于 2015-03-12T05:05:52.523 回答