Внедрение зависимостей в библиотеку классов

Имея решение с двумя проектами, я столкнулся с проблемой DI. Решение состоит из библиотеки классов и приложения WebApi2 (которое использует библиотеку классов и предоставляет API).

Я определил Autofac.module в библиотеке классов, который устанавливает все DI в проекте.

В проекте WebApi2 я создаю контейнер DI (используя Autofac.WebApi2) и загружаю модуль из библиотеки классов. Теперь, когда контроллеры API в проекте WepApi2 запрашивают службы в библиотеке классов, они создаются со всеми своими зависимостями, все это прекрасно работает!

Проблема в том, что теперь мне в библиотеке классов нужно создать экземпляр некоторых классов из строки (которая в конечном итоге поступает из БД), насколько я знаю, единственный способ сделать это с помощью отражения, поэтому я делаю так:

var ruleType = Type.GetType(rule.RuleImplementation.Implementation);
var rule = (IRule)Activator.CreateInstance(ruleType,param1,param2);

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

Можно ли каким-то образом использовать отражение и autoFac вместе для создания экземпляров объектов? Мне все еще нужно иметь возможность передавать свои параметры в объект.

... Или есть способ как-то получить доступ к контейнеру (который был создан в сборке webApi2) и использовать его для его разрешения? Я предполагаю, что это будет своего рода шаблон обслуживания, который, как я считаю, считается анти-шаблоном.

Как мне поступить? Весь вклад очень ценится.


person iCediCe    schedule 02.12.2016    source источник


Ответы (2)


Создание компонентов с использованием Activator.CreateInstance — плохая идея, потому что в основном это означает, что вы повторно реализуете логику, которую контейнер делает за вас, но без функций и средств защиты, которые предоставляет вам библиотека DI.

Я определил Autofac.module в библиотеке классов, который устанавливает все DI в проекте.

Вот где источник вашей беды. В приложении должно быть только одно место, где создаются (и регистрируются) графы объектов, и это Корень композиции. Этот корень композиции можно рассматривать как отдельный уровень, который находится поверх вашего уровня представления (веб-API), хотя довольно часто и корень композиции (уровень), и уровень представления находятся в одном проекте.

Это устраняет проблему, потому что в корне вашей композиции у вас уже есть доступ к контейнеру.

Хорошее решение, позволяющее создавать правила с использованием некоторого определения, полученного из базы данных, заключается в определении абстракции IRuleActivator. Эта абстракция может быть определена в вашей библиотеке и реализована внутри корня композиции. Это позволяет реализации обернуть контейнер, в то время как библиотека не обращает внимания на существование контейнера:

// Defined in the library
public interface IRuleActivator
{
    IRule GetRule(RuleData rule);
}

// Defined in the Compostion Root
public sealed class AutofacRuleActivator : IRuleActivator
{
    private readonly IComponentContext context;
    public AutofacRuleActivator(IComponentContext context) {
        this.context = context;
    }

    public IRule GetRule(RuleData rule) {
        Type ruleType = Type.GetType(rule.RuleImplementation.Implementation);
        return (IRule)this.context.Resolve(ruleType);
    }
}
person Steven    schedule 02.12.2016

Вы можете создать фабрику для создания IRule:

public IRuleFactory
{
   IRule CreateRule(params);
}

Для параметров я бы выбрал какой-то IParamsProviders, который затем можно внедрить в реализацию IRuleFactory, RuleImpl - это просто ваша реализация конструкции объекта, также может быть отражением:

public class RuleFactory : IRuleFactory
{
    public RuleFactory(IParamsProvider provider)
    {
        ...
    }

    public IRule CreateRule()
    {
        return new RuleImpl(provider.Param1, provider.Param2);
    }
}

Зарегистрируйте эти интерфейсы в Ioc и внедрите их в обработчик службы.

person Johnny    schedule 02.12.2016
comment
Мне нравится это решение. Насколько я вижу, единственным недостатком является то, что я должен вводить все правила в свою службу независимо от того, нужны они или нет, потому что я сначала узнаю об этом во время выполнения, когда я запрашиваю БД. - person iCediCe; 05.12.2016