#c# #dependency-injection
#c# #внедрение зависимостей
Вопрос:
Я использую Munq в качестве контейнера DI в проекте MVC3. У меня есть уровень сервиса, который извлекает DTO из репозитория. В зависимости от свойства в этом DTO мне нужно использовать одну из двух стратегий для выполнения вычислений в DTO. Я могу зарегистрировать именованный тип в контейнере, например
Container.Register<ICalculation>("Type1", c => new Type1Calculation);
Container.Register<ICalculation>("Type2", c => new Type2Calculation);
Тогда я могу напрямую ссылаться на контейнер при попытке создать экземпляр соответствующей стратегии, например
var calc = Container.Resolve<ICalculation>(dto.ServiceType);
Но это оставляет меня с зависимостью от самого контейнера с соответствующими проблемами связи и тестирования. Что мне нужно передать в конструктор сервиса, который позволил бы мне получить правильное вычисление, но без зависимости от контейнера? Должен ли я вместо этого передать фабрику, чтобы зависимость была на фабрике, а не в классе обслуживания?
Комментарии:
1. В качестве дополнения к вышесказанному я в настоящее время использую и рекомендую SimpleInjector simpleinjector.org для DI. Некоторая превосходная справочная информация доступна по адресу cuttingedge.it/blogs/steven/pivot/entry.php?id=91
Ответ №1:
Не уверен насчет Munq, но Autofac позволяет вам обходить Func
s, так что вы можете вообще пропустить все фабрики:
public class Foo
{
public Foo(Func<string, IBar> barFactory) { }
}
Проверьте, допускает ли Munq такое поведение.
В противном случае — да, вам придется прибегнуть к написанным вручную фабрикам, чтобы обеспечить еще один уровень косвенности.
Комментарии:
1. Как это работает в Autofac? Вы передаете функцию конструктору, который в вашем примере является заводским возвратом IBar. Является ли Foo классом обслуживания, и у него есть функция для разрешения в моем случае вычисления? Как это подключено в контейнере Autofac?
2. @David See nblumhardt.com/2010/01/the-relationship-zoo и code.google.com/p/autofac/wiki/RelationshipTypes . Вам не нужно делать ничего особенного — почти все делается самим Autofac.
3. Спасибо, Антон, но я все еще не совсем уверен, как это «пропустить все фабрики в целом». Разве ваша функция не является фабрикой? Чем это лучше, чем объявление заводского класса и внедрение его в конструктор? Можете ли вы привести пример функции BarFactory?
Ответ №2:
Я добавил к этому решение Munq. Сначала фабрика, которая включает интерфейс IDependencyResolver, позволяющий фабрике использовать контейнер для разрешения зависимостей в заводском методе:
public class CalculationFactory
{
private readonly IDependencyResolver _resolver;
public CalculationFactory(IDependencyResolver resolver)
{
ThrowIfNullArgument(resolver, "resolver", typeof(IDependencyResolver));
_resolver = resolver;
}
public static ICalculation CreateCalculator(int serviceType)
{
switch (serviceType)
{
case 1: return _resolver.Resolve<ICalculation>("Type1");
case 2: return _resolver.Resolve<ICalculation>("Type2");
default: return _resolver.Resolve<ICalculation>("InvalidType");
}
}
}
Затем в Global.asax.cs регистрируют соответствующие интерфейсы / классы, передавая контейнер на фабрику. Итак, теперь я могу настраивать свои тесты, и единственной дополнительной зависимостью является IDependencyResolver на заводе:
ioc.Register(c => new CalculationFactory(c));
ioc.Register<ICalculation>("Type1", c => c.Resolve<Type1Calculation>());
ioc.Register<ICalculation>("Type2", c => c.Resolve<Type2Calculation>());
ioc.Register<ICalculation>("InvalidType", c => c.Resolve<InvalidCalculation>());