2012-05-18 5 views
14

У меня есть объект IConfig, содержащий настройки, используемые во всем приложении. На данный момент, я впрыскивать весь объект в конструкторе каждого объекта, который нуждается в ней, а именно:Ninject: Bind Constructor Аргумент свойства другого объекта

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 содержит большое количество настроек, потому что это deserialised из общего конфигурационного файла, так инъекция всего объекта не требуется. То, что я хотел бы сделать, это изменить конструктор на 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 объекта и ввести свойства по мере необходимости, но объект, возвращаемый NinjectModule.Kernel не имеет Get<T>() метод.

ответ

14

Вы на правильном пути:

Метод Kernel.Get<T>() является метод расширения, определенный на ResolutionExtensions в Ninject namepsace так с добавлением 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

Я не могу заставить это работать. Объект 'IConfig', созданный в лямбда, содержит все значения по умолчанию (пустые строки для этих настроек). Если я просто сделаю обычный Kernel.Get , теперь, когда я добавил 'using Ninject', это работает правильно. Должен ли я инвестировать усилия, чтобы получить «контекстную» версию, есть ли какое-то преимущество? –

+0

Он должен работать с 'context.Kernel', я буду изучать его. Как оцениваются значения в «IConfig»?Вы используете несколько ядер? – nemesv

+0

Просто создал простой тестовый проект, и это действительно работает. Должно быть что-то конкретное для моего конкретного решения. Благодаря! –

1

Это может быть хорошим candiate для 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. Затем можно:

  1. Вводите ваш JsonConfig используя Ninject, вместо того, чтобы жестко закодированные имена параметров.
  2. Реализация/Mock a ICredentialConfig для создания экземпляра Foo в тестах, вместо того чтобы реализовать полный интерфейс IConfig.
Смежные вопросы