我有一个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
)。我想直接在我的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
对象的实例并根据需要注入属性,但{{1}返回的对象没有NinjectModule.Kernel
方法。
答案 0 :(得分:15)
你走在正确的轨道上:
Kernel.Get<T>()
方法是在ResolutionExtensions
namepsace中的Ninject
上定义的扩展方法,因此添加using 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);
答案 1 :(得分:1)
这对于Interface segregation principle来说可能是一个很好的结果。
在这种情况下,定义另一个界面,例如只包含ICredentialConfig
和Username
属性的Password
,然后让IConfig
实现此界面。
public Interface ICredentialConfig
{
string Username { get; }
string Password { get; }
}
public Interface IConfig : ICredentialConfig
{
//... other settings
}
现在让Foo
依赖于ICredentialConfig
而不是IConfig
。
然后你可以:
JsonConfig
,而不是使用硬编码的参数名称。ICredentialConfig
以在测试中实例化Foo
,而不必实现完整的IConfig
接口。