c # как инициализировать или установить значение конструктора по умолчанию для вставленной зависимости от класса - PullRequest
0 голосов
/ 03 июля 2019

У меня есть класс, который использует внедрение зависимостей:

public class MainClass: IDisposable
    {

        IUtilityRepository _utilityRepo = null;

        public MainClass()
        {

        }

        public MainClass(IUtilityRepository utilityRepo)
        {

            _utilityRepo = utilityRepo;

        }
}

Тогда мой класс UtilityRepository имеет:

 public class UtilityRepository : IUtilityRepository
    {
        private int _userId;
        private int _sessionId;

        // maybe have here some constructor where i can get some values from MainClass
       public  UtilityRepository ()
       {
          // here set the private properties from the MainClass when the dependency is intialized from MainClass
       }


        public List<string> MethodTestOne(string tempFolder)
        {
// here I want to 
        }

public List<string> MethodTestTwo(string tempFolder)
        {
        }
}

Что я хочу сделать, это передать из MainClass два свойствав UtilityRepository класс, так что любой метод в UtilityRepository может использовать эти значения глобально в классе без необходимости передачи значений каждому методу независимым способом.

Любая подсказка?

Ответы [ 2 ]

1 голос
/ 03 июля 2019

TL; DR: Мы должны внедрить зависимости в классы, которые в них нуждаются.Классы не должны нести ответственность за предоставление зависимостей своим зависимостям.

Если MainClass получает userId и sessionId откуда-то, как он их получает?Предположительно они вводятся в MainClass, или вводится что-то, что обеспечивает их.

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

public interface IContext // not the greatest name
{
    string UserId { get; }
    string SessionId { get; }
}

или

public interface IContextAccessor
{
    // where Context is an object containing the values you need.
    Context GetContext();
}

Затем вы конфигурируете контейнер, предоставляющий реализацию во время выполнения IContextAccessor, которая извлекает значения из текущего запроса.Внедрить IContextAccessor в UtilityRepository.

public class UtilityRepository : IUtilityRepository
{
    private readonly IContextAccessor _contextAccessor;

    public UtilityRepository(IContextAccessor contextAccessor)
    {
        _contextAccessor = contextAccessor;
    }
}

Если MainClass эти значения не нужны (он только получал их, поэтому мог передавать их чему-то другому), то он не должен их получать,Если MainClass нуждается в них, тот факт, что UtilityRepository также нуждается в них, является случайным.(Вы также можете ввести IContextAccessor в MainClass.) Нет причины, по которой MainClass должен отвечать за передачу их в UtilityRepository.

MainClass зависит от абстракции - IUtilityRepository.Он не знает и не должен знать, от чего зависит конкретная реализация этого интерфейса.Он не должен знать ничего, чего нет в этом интерфейсе.Как только он это сделает, он больше не «зависит» от интерфейса.Это связано с реализацией.

Это одно из основных преимуществ использования контейнера IoC.Классы получают свои зависимости от контейнера, а не друг от друга.Зависимость от абстракций означает, что классы не знают, как эти абстракции реализованы.Это, в свою очередь, означает, что классы не знают о зависимостях из их зависимостей.


Чтобы проиллюстрировать: лампа зависит от абстракции - розетка, которая получает свою мощностьот где-то. Это может быть электрическая сеть, солнечная батарея, генератор, велосипед или что-то еще.

Независимо от реализации источника питания, он, вероятно, имеет свои собственные зависимости.Генератору нужен бензин.Электросети нужна электростанция.

Эти зависимости, вероятно, имеют свои собственные зависимости.Электростанции нужен кто-то, чтобы копать уголь.

Лампа не должна ничего знать об этих зависимостях.Если используется лампа, значит:

  • Подключите лампу
  • Включите лампу
  • Подайте газ в генератор

Затеммы больше не зависим от абстракции.Мы зависим от генератора.Неважно, если мы объявим его как интерфейс и назовем его IPowerSupply.Единственная реализация, с которой он может работать, - это генератор.

То же самое относится и к электросети.Это зависит от источника питания.Для этого источника энергии могут потребоваться уголь, генераторы и т. Д. Но что произойдет, если мы возьмем на себя ответственность за запуск генератора, а затем позже заменим генератор на массивную панель солнечных батарей?Как электросеть запустит генератор, когда генератора нет?

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

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

0 голосов
/ 03 июля 2019

По сути, передача параметров конструктору реализации вводит связывание, которое противоположно принципу внедрения зависимостей.

Однако, если вы определите требование, чтобы каждая реализация получала в конструкторе два параметра userId и sessionId, разъединение сохраняется, поскольку все реализации придерживаются этого соглашения.

Чтобы включить такой сценарий, MainClass должен получить параметр IKernel, а не IUtilityRepository:

public class MainClass
{
    private readonly IUtilityRepository _utilityRepo = null;

    public MainClass()
    {
    }

    public MainClass(IKernel kernel)
    {
        var userId = new Ninject.Parameters.ConstructorArgument("userId", 123);
        var sessionId = new Ninject.Parameters.ConstructorArgument("sessionId", 456);

        _utilityRepo = kernel.Get<IUtilityRepository>(userId, sessionId);
    }
}

Таким образом UtilityRepository сможет получить двапараметры в конструкторе:

public class UtilityRepository : IUtilityRepository
{
    private readonly int _userId;
    private readonly int _sessionId;

    // maybe have here some constructor where i can get some values from MainClass
    public  UtilityRepository (int userId, int sessionId)
    {
        _userId = userId;
        _sessionId = sessionId;
    }

    public List<string> MethodTestOne(string tempFolder)
    {
        // do something...
    }

    public List<string> MethodTestTwo(string tempFolder)
    {
        // do something...
    }
}

Обратите внимание, что в таком случае регистрация UtilityRepository должна быть временной, так что новый экземпляр создается каждый раз, когда его запрашивают с конкретными значениями параметров:

kernel.Bind<IUtilityRepository>().To<UtilityRepository>().InTransientScope();

Недостатком этого решения является то, что MainClass больше не использует параметры конструктора для явного объявления его зависимостей.Код теперь менее самодокументирован.

...