Ninject: инъекция двух разных объектов одного типа
Если у меня есть класс, который имеет двойные зависимости одного и того же типа (нужны два разных экземпляра), если единственное различие между экземплярами - это более глубокая зависимость, каков наилучший способ заставить Ninject выполнять DI и сохранять эти два графика раздельными?
Пример граф объекта:
foo → ClassA → ClassB
bar → ClassA → ClassB
Учебный класс C
конструктор:
public class C
{
public C(ClassB foo, ClassB bar) { … }
}
Итак, как мне убедиться, что ClassB
созданный с foo
поставляется как ClassB
зависимостях foo
, а также bar
... bar
?
Просто для некоторых пределов у меня изменились требования, поэтому мне нужно заменить репозиторий только для записи (IAdd) на композитный репозиторий только для записи
public class CompositeWriteRepository<T> : IAdd<T>
{
public CompositeWriteRepository(IAdd<T> foo, IAdd<T> bar, Func<T, bool> descriminator) { ... }
public Add(T entity)
{
if (descriminator(entity)) {
foo.Add(entity);
} else {
bar.Add(entity);
}
}
}
С помощью насмешек это было достаточно просто, я мог бы просто ввести, используя имена:
kernel.Bind<IAdd<EntityType>>().To<fooRepository>().Named("foo");
kernel.Bind<IAdd<EntityType>>().To<barRepository>().Named("bar");
kernel.Bind<IAdd<EntityType>>().To<CompositeWriterRepository<EntityType>>()
.WithConstructorArgument("foo", x => x.Kernel.Get<IAdd<EntityType>>("foo")
.WithConstructorArgument("bar", x => x.Kernel.Get<IAdd<EntityType>>("bar");
Проблема возникает, когда я использую настоящие репозитории; foo
а также bar
в конечном итоге записывать в файлы, поэтому им нужны разные имена файлов. Так как они StreamWriter
репозитории, одна из их зависимостей фактически получает два разных имени файла.
string FileName → FileStreamWriterFactory → StreamRepository → CompositeRepository
Единственный способ, который я нашел до сих пор, - это создать именованный FileName
по имени FileStreamWriterFactory
по имени StreamRepository
× 2 (один раз для foo
и один раз для bar
). Это похоже на большую работу, поэтому я надеюсь, что есть лучшее решение.
Я могу при необходимости изменить архитектуру, это было похоже на элегантный способ быстро добавить разделение записей, когда требования изменились. Я понимаю, что все мои классы довольно специфичны, но я думаю, что в целом это поддержит одна ответственность, и в моем случае мы пишем несколько небольших приложений и имеем больше запросов, которые мы можем выполнить, так что это хороший способ получить много повторно используемого кода. Вокруг этого мне в основном просто нужно переконфигурировать для разных задач.
Решение
Remo Gloor должен получить кредит; это, наверное, лучшая практика.
На самом деле я создал новое расширение
public static bool WhenAnchester(this IRequest request, Func<IRequest, bool> conditions)
{
var parentContext = request.ParentContext;
if (parentContext == null) {
return false;
}
return conditions(parentContext.Request) ||
parentContext.Request.WhenAnchester(conditions);
}
Это позволяет мне легко контролировать, какой файл будет вставлен в какой репозиторий.
kernel.Bind<string>().ToConstant("Foo.txt")
.When(x => x.Target.Name == "filepath" &&
x.WhenAnchester(t => t.Target != null && t.Target.Name == "Dest1"));
kernel.Bind<string>().ToConstant("Bar.txt")
.When(x => x.Target.Name == "filepath" &&
x.WhenAnchester(t => t.Target != null && t.Target.Name == "Dest2"));
Вероятно, есть лучшее решение, так что я бы не стал рекомендовать его другим, но оно мне подходит.
1 ответ
Да есть лучший способ
См. https://github.com/ninject/ninject/commit/60443badf4ef840531c93e9287b154a9bba337c2
Это 3.0, но IsAnyAnchestorNamed также можно использовать с 2.2 из условия "Когда".