Подтвердить что ты не робот

Удалить зависимость от контейнера IoC

После того, как я прочитал все больше о контейнерах IoC, я прочитал этот пост об отсутствии IoC.Resolve() и т.д. в вашем коде.

Мне действительно интересно узнать, как я могу удалить зависимость от контейнера?

Я хочу написать код следующим образом:

public void Action()
{
    using(IDataContext dc = IoC.Resolve<IDataContext>())
    {
        IUserRepository repo = IoC.Resolve<IUserRepository>();
        // Do stuff with repo...
    }
}

Но как я могу избавиться от вызовов IoC.Resolve? Может быть, мне нужно лучшее понимание DI...

Спасибо заранее.

4b9b3361

Ответ 1

Вообще говоря, большинство зависимостей могут быть введены в ваш класс в момент его создания. Однако в этом конкретном случае вам нужен компонент, который должен быть создан по требованию во время использования. В таких случаях очень сложно полностью удалить зависимость от контейнера IoC. Мой подход всегда заключался в создании factory, который вводится в класс во время создания, что, в свою очередь, инкапсулирует все прямое использование IoC. Это позволяет замалчивать ваши фабрики для тестирования, а не сам контейнер IoC..., который, как правило, намного проще:

// In Presentation.csproj
class PresentationController
{
    public PresentationController(IDataContextFactory dataContextFactory, IRepositoryFactory repositoryFactory)
    {
        #region .NET 4 Contract
        Contract.Requires(dataContextFactory != null);
        Contract.Requires(repositoryFactory != null);
        #endregion

        _dataContextFactory = dataContextFactory;
        _repositoryFactory = repositoryFactory;
    }

    private readonly IDataContextFactory _dataContextFactory;
    private readonly IRepositoryFactory _repositoryFactory;

    public void Action()
    {
        using (IDataContext dc = _dataContextFactory.CreateInstance())
        {
            var repo = _repositoryFactory.CreateUserRepository();
            // do stuff with repo...
        }
    }
}

// In Factories.API.csproj
interface IDataContextFactory
{
    IDataContext CreateInstance();
}

interface IRepositoryFactory
{
    IUserRepository CreateUserRepository();
    IAddressRepository CreateAddressRepository();
    // etc.
}

// In Factories.Impl.csproj
class DataContextFactory: IDataContextFactory
{
    public IDataContext CreateInstance()
    {
        var context = IoC.Resolve<IDataContext>();
        // Do any common setup or initialization that may be required on 'context'
        return context;
    }
}

class RepositoryFactory: IRepositoryFactory
{
    public IUserRepository CreateUserRepository()
    {
        var repo = IoC.Resolve<IUserRepository>();
        // Do any common setup or initialization that may be required on 'repo'
        return repo;
    }

    public IAddressRepository CreateAddressRepository()
    {
        var repo = IoC.Resolve<IAddressRepository>();
        // Do any common setup or initialization that may be required on 'repo'
        return repo;
    }

    // etc.
}

Преимущество такого подхода заключается в том, что, хотя вы не можете полностью исключить зависимость IoC, вы можете инкапсулировать его в один вид объекта (a factory), развязывая основную часть вашего кода из контейнера IoC. Это повышает гибкость ваших кодов в свете, скажем, перехода от одного контейнера IoC к другому (например, Windsor to Ninject).

Следует отметить, что интересным последствием этого является то, что ваши заводы обычно вводятся в их иждивенцы той же картой IoC, которую они используют. Например, если вы используете Castle Windsor, вы должны создать конфигурацию, которая сообщает контейнеру IoC вставлять две фабрики в ваш бизнес-компонент при его создании. Сам бизнес-компонент также может иметь factory... или его можно просто ввести одной картой IoC в компонент более высокого уровня и т.д. И т.д., Ad inf.

Ответ 2

Недавно я был на проекте, который не был установлен на контейнере IoC. Они справлялись с неопределенностью, отказываясь от специфических особенностей своего контейнера, отличных от IoC, а также путем упаковки Resolve своим собственным классом. Это также то, что я видел несколько раз в блогах... удаляю последнюю зависимость, зависимость от контейнера инъекции зависимостей.

Это эффективный метод, но в какой-то момент вам нужно выбрать инструменты, которые вы используете, и быть готовыми принять то, что вы будете оплачивать расходы, чтобы переключиться на альтернативные инструменты. Для меня контейнеры IoC относятся к категории вещей, которые вы, вероятно, должны всерьез обнимать, поэтому я задаю вопрос об этом уровне факторинга. Если вы хотите изучить это дальше, я предлагаю следующую ссылку:

http://blog.objectmentor.com/articles/2010/01/17/dependency-injection-inversion

Ответ 3

Один из вариантов заключается в повторной записи метода для принятия делегатов Func<T>. Это устраняет зависимость от метода и позволяет unit test с помощью mock:

public void Action(Func<IDataContext> getDataContext, Func<IUserRepository> getUserRepository)
{
    using(IDataContext dc = getDataContext())
    {
        IUserRepository repo = getUserRepository();
        // Do stuff with repo...
    }
}

Ответ 5

Имейте второй инжектор зависимостей для ввода первого и сначала введите второй.