在 F# 中使用“IHttpClientFactory”的常规方法是什么?
What's the conventional way to use `IHttpClientFactory` in F#?
所以在 C# .NET Core 应用程序中,我可以使用所有 fancy DI stuff 来缓存 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 带到这里,但大师 don't encourage 这样的想法所以我只是想知道是否有一些完全不同的超级功能方法。
规范答案:通常如果你想reuse/cache一些东西,你会把它作为参数传递给你的函数:
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) |> ...
// And later:
let clientFactory () = magic.MakeClient() // <- insert caching behavior here
let isReachable = isReachable' clientFactory
实际答案:当然,上面的扩展性不好。例如,现在需要使用 isReachable
的人不能直接从静态上下文中调用它,他们必须从调用它们的人那里获取它作为参数,等等。不过,这并不意味着您必须在任何地方都通过隧道。您可以部分应用所有可以在程序初始化时部分应用的东西。这等同于手动实现一个 IoC 容器,除了不是指定 container.Add<IHttpClient, HttpClient>
或其他任何内容,而是传递函数。
这是可行的。我已经做了。但随着时间的推移它确实变得有点丑陋。
所以实用的答案是:宗教是为了精神成长,其他一切都使用常识。
完全可以偏离神圣的功能原则并使用 IoC 容器、接口或其他任何东西。毕竟,接口是 F# 实现更高级别行为的唯一机制,为什么不呢?
这尤其正确,因为大多数“框架”(如 WPF、ASP.NET 等)本质上都是面向对象的,因此您会无缘无故地试图避免这种情况。
我个人采用的一般规则是:用函数式风格表达你的实际业务逻辑,尽可能清晰和纯粹,但在与外部的边界上世界(即 OS 或您正在使用的任何框架)随意做外界希望您做的任何事情。
适用于您的特定示例,isReachable
功能非常明显属于与外界的边界。所以请随意以任何适合的方式实施它。
所以在 C# .NET Core 应用程序中,我可以使用所有 fancy DI stuff 来缓存 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 带到这里,但大师 don't encourage 这样的想法所以我只是想知道是否有一些完全不同的超级功能方法。
规范答案:通常如果你想reuse/cache一些东西,你会把它作为参数传递给你的函数:
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) |> ...
// And later:
let clientFactory () = magic.MakeClient() // <- insert caching behavior here
let isReachable = isReachable' clientFactory
实际答案:当然,上面的扩展性不好。例如,现在需要使用 isReachable
的人不能直接从静态上下文中调用它,他们必须从调用它们的人那里获取它作为参数,等等。不过,这并不意味着您必须在任何地方都通过隧道。您可以部分应用所有可以在程序初始化时部分应用的东西。这等同于手动实现一个 IoC 容器,除了不是指定 container.Add<IHttpClient, HttpClient>
或其他任何内容,而是传递函数。
这是可行的。我已经做了。但随着时间的推移它确实变得有点丑陋。
所以实用的答案是:宗教是为了精神成长,其他一切都使用常识。
完全可以偏离神圣的功能原则并使用 IoC 容器、接口或其他任何东西。毕竟,接口是 F# 实现更高级别行为的唯一机制,为什么不呢?
这尤其正确,因为大多数“框架”(如 WPF、ASP.NET 等)本质上都是面向对象的,因此您会无缘无故地试图避免这种情况。
我个人采用的一般规则是:用函数式风格表达你的实际业务逻辑,尽可能清晰和纯粹,但在与外部的边界上世界(即 OS 或您正在使用的任何框架)随意做外界希望您做的任何事情。
适用于您的特定示例,isReachable
功能非常明显属于与外界的边界。所以请随意以任何适合的方式实施它。