Какой обычный способ использовать `IHttpClientFactory` в F #? - PullRequest
1 голос
/ 28 января 2020

Так что в C#. NET Базовых приложениях я могу использовать все причудливые элементы DI для кеширования HttpClient, чтобы избежать исчерпания сокетов, оптимизации соединения с сервером и т. Д.:

services.AddHttpClient<IRandomService, RandomService>();

А теперь в F # ... У меня просто есть простая функция в консольном приложении, которая проверяет действительность URL-адреса и проверяет циклы по различным URL-адресам:

let isReachable (url: string) = 
    (new HttpClient()).GetAsync(url) 
    |> Async.AwaitTask 
    |> Async.RunSynchronously
    |> fun response -> response.IsSuccessStatusCode

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

1 Ответ

2 голосов
/ 28 января 2020

Канонический ответ : Обычно, если вы хотите что-то повторно использовать / кэшировать, вы должны передать это в качестве параметра своей функции:

let isReachable' (client: HttpClient) url = 
    client.GetAsync(url) |> ...

Обратите внимание на галочку после имени функции , Это сделано специально, потому что тогда я буду частично применять его так:

let oneTrueHttpClient = new HttpClient()
let isReachable = isReachable' oneTrueHttpClient

, возвращая мою первоначальную функцию isReachable с одним параметром, но теперь все вызовы используют один и тот же экземпляр HttpClient .

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

Конечно, вы можете захотеть иметь Вместо этого пул HTTP-клиентов, по одному для каждого потока или что-то в этом роде. В этом случае вы бы использовали фабричную функцию вместо самого экземпляра:

let isReachable' (getClient: unit -> HttpClient) url = 
    getClient().GetAsync(url) |> ...

let clientFactory () = magic.MakeClient()  // <- insert caching behavior here
let isReachable = isReachable' clientFactory

Практический ответ : Но, конечно, вышеприведенное плохо масштабируется. Например, теперь тот, кому нужно использовать isReachable, не может просто вызвать его из контекста stati c, он должен получить его как параметр от того, кто его вызывает, и так далее, и так далее. Это не значит, что вы должны проходить через абсолютно все везде. Вы можете частично применить все, что можно частично применить при инициализации программы. Это было бы эквивалентно ручной реализации контейнера Io C, за исключением того, что вместо указания container.Add<IHttpClient, HttpClient> или чего-либо другого вы передаете функции.

Это выполнимо. Я сделал это. Но со временем это становится ужасно.

Итак, практический ответ : религия - для духовного роста, для всего остального - здравый смысл.

Это вполне нормально отойдите от Священных Функциональных Принципов и go используйте контейнер Io C, или интерфейсы, или что-то еще. В конце концов, интерфейсы - это единственный механизм, который F # имеет для достижения поведения более высокого ранга, так почему бы и нет?

Это особенно верно, потому что большинство "фреймворков", таких как WPF, ASP. NET, et c. изначально являются объектно-ориентированными, поэтому вы будете прыгать через обручи, пытаясь избежать этого без веской причины.


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

Применительно к вашему конкретному примеру, функция isReachable очень четко относится к границе с внешним миром. Так что не стесняйтесь применять его любым удобным для вас способом.

...