Несколько областей применения в приложении с использованием Autofac - PullRequest
0 голосов
/ 13 января 2020

У меня вопрос по поводу продолжительности жизни в Autofa c. Разрешено создавать несколько областей в приложении в разных местах для разрешения типов?

В официальной документации есть такое утверждение:

Примечание: вообще говоря, расположение службы в значительной степени считается антишаблоном То есть ручное создание областей повсеместно и использование контейнера через ваш код не обязательно является лучшим способом для go. Используя библиотеки интеграции Autofa c, вам обычно не нужно делать то, что мы делали в примере приложения выше. Вместо этого все решается из центрального, верхнего уровня в приложении, и ручное разрешение редко. Конечно, как вы разрабатываете свое приложение, зависит только от вас.

Что если у меня есть несколько моделей представлений, и мне нужно разрешить несколько типов в каждой из них?

Пример:

    public class ClassA
    {
        //some code here

    }

    public class ClassB
    {
        //some code here

    }

    public class ClassC
    {
        //some code here

    }

    public class ViewModelA
    {
        public ViewModelA()
        {

        }


        public void Method()
        {
            //some code here
            using (var scope = Container.BeginLifetimeScope())
            {
                var typeC = scope.Resolve<ClassC>();
                //some code here
            }
        }

    }

    public class ViewModelB
    {
        public ViewModelB()
        {

        }

        public void Method()
        {
            //some code here
            using (var scope = Container.BeginLifetimeScope())
            {
                var typeA = scope.Resolve<ClassA>();
                var typeB = scope.Resolve<ClassB>();
                //some code here
            }
        }
    }

Предполагая, что все типы зарегистрированы в контейнере - является ли хорошей практикой распространение таких областей в приложении? Как вы это понимаете?

С уважением.

1 Ответ

0 голосов
/ 17 января 2020

TL; DR

У меня есть вопрос по поводу продолжительности жизни в Autofa c. Разрешено создавать несколько областей в приложении в разных местах для разрешения типов?

Да, это разрешено, хотя считается не лучшим способом для go - но определенно не запрещенная.

Что если у меня есть несколько моделей представлений, и мне нужно разрешить несколько типов в каждой?

Просто разрешите их с помощью конструктора инъекция, как это было предложено @Raymond. Вам не нужны области видимости для чего-то такого простого, как простое решение проблем.

Теперь давайте немного поговорим об этом.

Прежде всего, как вы используете инжектор конструктора? Нет ничего проще, чем это. Давайте немного изменим ваш пример:

public class ClassA
{
    //some code here

}

public class ClassB
{
    //some code here

}

public class ClassC
{
    //some code here

}

public class ViewModelA
{
    private ClassC typeC;

    // this is a constructor injection - yes, it's that simple.
    public ViewModelA(ClassC _typeC)
    {
        typeC = _typeC;
    }


    public void Method()
    {
        typeC.doStuff();
    }

}

public class ViewModelB
{
    private ClassA typeA;
    private ClassB typeB;

    public ViewModelB(ClassA _typeA, ClassB _typeB)
    {
        typeA = _typeA;
        typeB = _typeB;
    }

    public void Method()
    {
        typeA.doStuff();
        typeB.doAnotherStuff();
    }
}

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

Что это нам дает? Что ж, теперь классы ViewModelA и ViewModelB ничего не знают ни о каких контейнерах или DI вообще. Все, что они знают, это то, что кто-то (читай: «контейнер») каким-то образом предоставит пару параметров указанных типов (которые обычно являются интерфейсами) извне. Сам класс теперь ничего не знает о том, кто будет создавать свои зависимости, где, когда и как - это его больше не касается. В этом весь смысл внедрения зависимостей: устранение проблемы управления зависимостями.

Следующее - где области видимости на этом рисунке? Ну ... никуда. :) Это идеальная картина: сервисы не знают о существовании контейнера DI.

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

Давайте рассмотрим чрезвычайно упрощенный пример. Скажем, у вас есть работа, которая должна выполнять какие-то запросы к внешним ресурсам по расписанию. Однако метод, который выполняет запросы, на самом деле является обобщенным c: он не знает заранее, какой именно тип запроса он выполняет, поскольку он определяется параметром типа метода, который будет известен только во время выполнения.

Кроме того, допустим, у вас есть другое требование: вам нужно не хранить API-сервисы в памяти, а создавать их только для выполнения запроса, а затем сразу же отбрасывать их после выполнения работы.

Это может выглядеть примерно так:

abstract class SomeSpecialApi<TSomeTypeParam> { // just some common base type
    public abstract string doGetStuff();
}

class SomeSpecialApi1 : SomeSpecialApi<SomeTypeParam1> where SomeTypeParam1 : TSomeTypeParam {

    private HttpClient _client;

    public SomeSpecialApi(HttpClient client) {
        _client = client;
    }

    public override string doGetStuff() {
        return _client.get(someUrlWeDontCareAbout).Result;
    }

}

class SomeSpecialApi2 : SomeSpecialApi<SomeTypeParam2> where SomeTypeParam2 : TSomeTypeParam {

    private SomeFtpClient _client;

    public SomeSpecialApi(SomeFtpClient client) {
        _client = client;
    }

    public override string doGetStuff() {
        // it's just something very different from the previous class
        return _client.read(someOtherPathWeDontCareAboutEither).Result;
    }

}

class JobPerformer {

    private ILifeTimeScope _scope;

    public JobPerformer(ILifeTimeScope scope) {
        _scope = scope;
    }

    void PerformJob<T>() {
        while (true) {
            using (var childScope = scope.BeginLifeTimeScope()) {
                var api = childScope.Resolve<SomeSpecialApi<T>>();
                var result = api.doGetStuff();
                // do something with the result
            } // here the childScope and everything resolved from it will be destroyed
            // and that's the whole purpose of the lifetime scopes.
            Thread.Sleep(1000);
        }
    }

}

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

Однако недостатком здесь является то, что теперь класс JobPerformer должен знать о такой вещи, как ILifeTimeScope. Так что в некотором смысле это похоже на шаблон ServiceLocator: мы должны знать как минимум , кто запрашивает зависимость, другими словами: теперь JobPerformer знает что-то о нашем контейнере. Это то, чего мы в идеале хотим избежать, и для этого есть и другие способы: фабрики; собственные экземпляры; разные жизненные политики регистрации услуг в контейнере; разные виды прижизненных прицелов (по крайней мере у autofa c); и др c.

...