Внедрение зависимостей C # и шаблона стратегии

#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>());