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.