Зная, что Func<B>
по сути является "ярлыком" для myCurrentLifetimeScope.Resolve<B>
, обернутого в объект, не относящийся к Autofac, это может помочь перевернуть этот вопрос и спросить, почему Func<B>
не уважать области действия на весь срок службы и какие последствия это имело бы, если бы не .
Подавляющее большинство приложений, использующих области действия на весь срок службы, делают это для изоляции единиц работы. Например, веб-приложения, которые имеют области действия для каждого запроса. Существует множество веских причин, по которым этот интересный шаблон, например, возможность создавать только ресурсы, необходимые для данного запроса, а затем очищать их по окончании запроса, - использовать только необходимую память.
Учитывая это, скажем, что, вообще говоря, области действия на всю жизнь интересно иметь, , если для чего-то, кроме хорошего способа отследить и очистить выделенные ресурсы.
Теперь допустим, что у вас естьсоединение с веб-службой или чем-то вроде того. Может быть, это сервис WCF. Есть некоторые вещи, которые вы можете помнить о клиентах службы WCF:
- После сбоя канала клиент становится бесполезным. Вы не можете просто создать одного клиента, потому что, если служба когда-либо повреждает канал на вас, вы должны выбросить этот экземпляр клиента и создать новый.
- Вам необходимо избавиться от клиентов, когда высделанный. Они могут держать каналы открытыми и поглощать ресурсы, если вы этого не сделаете.
Учитывая, что Func<IServiceClient>
отношения становятся очень интересными.
Допустим, у вас естьMVC контроллер. Это своего рода псевдокод, я не собираюсь запускать его через компилятор.
public class MyController : Controller
{
private readonly Func<IServiceClient> clientFactory;
public MyController(Func<IServiceClient> clientFactory)
{
this._clientFactory = clientFactory;
}
public string GetSomethingReallyCool()
{
var retryCount = 0;
while(retryCount < 5)
{
var client = this._clientFactory();
try
{
return client.GetSomethingCool();
}
catch
{
retryCount++;
}
}
return "We failed to get the cool data.";
}
}
У нас есть своего рода попытка бедного человека, встроенная здесь, потому что служба ненадежна. Я не рекомендую этот точный код, просто быстро и легко продемонстрировать концепцию.
Сцена установлена! Теперь, ради обсуждения, давайте представим, что отношения не учитывали прижизненные рамки, на мгновение.
Что происходит во время веб-запроса?
- Ваш контроллер MVC разрешается из области времени жизни для каждого запроса.
- Контроллер принимает
Func<IServiceClient>
функцию для создания - динамически - клиента службы WCF, когда это необходимо. - Действие контроллерасоздает сервисный клиент и открывает канал. Этот сервисный клиент живет в корневом контейнере , что означает, что он никогда не будет удален до тех пор, пока все приложение не будет закрыто.
- Сбой службы. Канал неисправен, а не закрыт. Это будет просто зависать ... но вы не можете использовать один и тот же экземпляр клиента службы. Это тост.
- Действие контроллера создает другого клиента службы и открывает канал. Этот сервисный клиент также находится в корневом контейнере и будет выделен навсегда.
- Сервисный вызов успешно завершен, и возвращается значение. Но в то время как клиенты выходят за рамки, они одноразовые - контейнер перерабатывает отходы, верно? Таким образом, местные жители находятся вне области видимости, но они все еще выделены.
Это довольно ужасная утечка памяти .
Включите нормальное поведениеобратно! Мы закончили притворяться!
Если он подчиняется областям действия на весь срок службы, он работает следующим образом:
- Ваш контроллер MVC разрешается из области времени действия каждого запроса.
- Контроллер использует функцию
Func<IServiceClient>
для динамического создания клиента службы WCF, когда это необходимо. - Действие контроллера создает клиента службы и открывает канал. Этот клиент службы проживает в пределах срока действия запроса , что означает, что он будет удален после завершения запроса.
- Ошибка службы. Канал неисправен, а не закрыт. Это будет просто зависать ... но вы не можете использовать один и тот же экземпляр клиента службы. Это тост.
- Действие контроллера создает другого клиента службы и открывает канал. Этот сервисный клиент также находится в области запроса и будет расположен в конце запроса.
- Сервисный вызов успешно завершен, и значение возвращается.
- ЗапросОбласть действия времени жизни заканчивается и удаляет клиентов - закрывая каналы, освобождая ресурсы.
Нет утечки памяти, хорошая очистка.
Другой сценарий - Допустим, вы регистрировать вещи при создании областей действия времени жизни .
var builder = new ContainerBuilder();
builder.RegisterType<Alpha>();
var container = builder.Build();
using(var scope = container.BeginLifetimeScope(b => b.RegisterType<Beta>())
{
var f = scope.Resolve<Func<Beta>>();
// f is a Func<Beta> - call it, and you should get a B.
// If Func<Beta> doesn't respect lifetime scopes, what
// does this yield?
var beta = f();
}
На самом деле это общий шаблон для таких вещей, как интеграция с WebAPI - приходит сообщение с запросом, а когда создается область действия запроса, создается запроссообщение динамически регистрируется в области запроса. Он не существует на глобальном уровне.
Из некоторых из них вы, вероятно, можете видеть, что единственный логический способ для Func<B>
вести себя - это подчиняться жизненным границам. Это простоне работает, если это не так.