Требовать фабричный класс в методе расширения - PullRequest
2 голосов
/ 26 января 2012

Я работаю над большим унаследованным приложением C #, и передо мной поставлена ​​задача удалить все случаи использования статического фабричного класса ServiceLocator.GetObject<T>() и заменить его инжектированными в конструктор зависимостями.

По большей части это просто, однако в базе кода приложения имеется около 50 случаев, где это немного сложно. Например, Servicelocator используется в статическом классе, или в методе расширения, или даже в WPF MarkupExtension !.

Например, если бы вы столкнулись с таким фрагментом кода, что бы вы сделали? (кроме плача)

public static class MyExtensions
{
    private static ISingletonServiceOne _ServiceOne = null;
    private static ISingletonServiceTwo _ServiceTwo = null; // etc ... 

    public static SummaryHeader GetBannerSummary(this IModel rfq, object requester)
    {
        Guard.ArgumentNotNull(rfq, "rfq");
        Guard.ArgumentNotNull(requester, "requester");

        if (_ServiceOne == null)
        {
            _ServiceOne = ServiceLocator.GetService<ISingletonServiceOne>(requester);
            Guard.ArgumentNotNull(_ServiceOne, "_ServiceOne");
        }

        return _ServiceOne.GetBannerSummary(rfq);
    }

В приведенном выше метод ServiceLocator.GetObject () использовался в методе расширения в IModel для определения местоположения зарегистрированной службы-одиночки и выполнения метода с использованием IModel.

Вопрос:

  • Существуют ли какие-либо шаблоны / методы, позволяющие избежать такого рода вещей - контейнер DI, необходимый для статического класса, преобразователя значений или метода расширения
  • Существуют ли какие-либо шаблоны / методы для работы с циклическими зависимостями в DI?
  • Что бы вы сделали в приведенном выше, если есть компромисс между хорошим кодом и временем доставки?

Я думаю переместить метод GetBannerSummary () из расширений и только IModel в этом случае, однако (не смейтесь), есть также случаи, когда тот же ServiceLocator используется в ValueConverters (WPF) и MarkupExtensions: 0

Ваши комментарии / предложения приветствуются

Ответы [ 2 ]

2 голосов
/ 26 января 2012

Можно ли добавить IServiceX в ваши классы вместо использования статического класса доступа? Возможно, сделать GetBannerSummary метод частью абстрактного базового класса, который реализует IModel?

DI не летает, когда вы не управляете созданием объекта. Триггеры, Поведения или Расширения разметки в WPF попадают в эту категорию. Там нет возможности использовать ServiceLocator там.

2 голосов
/ 26 января 2012

Единственный раз, когда я бы использовал ServiceLocator, это статические методы, например методы расширения и IValueConverters, поскольку, к сожалению, действительно нет другого способа получить зависимости.

Единственное (немного) лучшее решение - переместить вызов ServiceLocator в лениво загруженное свойство, чтобы зависимость могла быть введена во время модульного тестирования.

В вашем случае, однако, это не сработает, поскольку у вас есть свойство requester, передаваемое в GetService, и в этом случае вам в идеале нужно добавить зависимость IServiceOneFactory, в которую вы можете передать свой объект requestter. Итак:

public interface IServiceOneFactory
{
    ISingletonServiceOne Create(object requester);
}

public static class MyExtensions
{
    public static IServiceOneFactory ServiceOneFactory
    {
        get 
        {
            if( _ServiceOneFactory==null)
                _ServiceOneFactory = ServiceLocator.GetService<IServiceOneFactory>();
            return _ServiceOneFactory;
        }
        set { _ServiceOneFactory = value; }
    }

    private static IServiceOneFactory _ServiceOneFactory = null;
    private static ISingletonServiceOne _ServiceOne = null;
    private static ISingletonServiceTwo _ServiceTwo = null; // etc ... 

    public static SummaryHeader GetBannerSummary(this IModel rfq, object requester)
    {
        Guard.ArgumentNotNull(rfq, "rfq");
        Guard.ArgumentNotNull(requester, "requester");

        if (_ServiceOne == null)
        {
            _ServiceOne = ServiceOneFactory.Create(requester);
            Guard.ArgumentNotNull(_ServiceOne, "_ServiceOne");
        }

        return _ServiceOne.GetBannerSummary(rfq);
    }
}
...