- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 F# 和 .NET Core 构建一个 GraphQL 服务器。为了实现批处理和地址 N+1 选择,我正在构建一个数据加载器。 Facebook 的dataloader使用 Node.js 事件循环滴答 来收集和分派(dispatch)批量请求。
但是,这种机制在 .NET Core 中不可用。我知道我可以从可以手动调用的数据加载器实例中实现 run/dispatch
方法。但是,在独立执行的解析器中很难做到这一点。所以我需要一些自动调度机制来运行批处理请求。
关于如何实现它有什么建议吗?
最佳答案
这是一个很好的问题,我最近问自己。
有一些用于 F# 和 .NET 的“数据加载器”类型库,但是如果您还使用 FSharp.Data.GraphQL
,那么能够很好集成的解决方案就更少了。
请注意,“Haxl”方法不能(轻松地)用于 FSharp.Data.GraphQL
。这是因为 Haxl 类型必须集成到 GraphQL 查询模型中,但 FSharp.Data.GraphQL
只理解同步和 异步
。
我能找到的最合适的实现在 FSharp.Core.Extensions
中.这是一个相当新的库,但它质量很高并且已获得 Apache 2.0 许可。
我确信有很多方法可以将它集成到 FSharp.Data.GraphQL
中,但是我的首选方法是将数据加载器放入模式的根值中。这允许树下的所有 GraphQL 解析器访问它。
我认为最好的解释方式是举个例子。
这里我们有一个“People”域,可以有零个或多个“followers”,他们也是“People”。每个人都有一个全局唯一的 ID。人与人之间的关注者有很大的重叠,所以一个天真的解决方案可能会重复地重新获取相同的数据。我们的数据库层可以在一次查询中获取许多人的记录,因此我们希望尽可能利用这一点。
您可以将此代码粘贴到 .fsx
文件中并运行它。依赖项由 Paket 获取。
paket.dependencies
generate_load_scripts: true
source https://www.nuget.org/api/v2
source https://api.nuget.org/v3/index.json
storage: none
framework: net5.0, netstandard2.1
nuget FSharp.Core 5.0.0
nuget FSharp.Data.GraphQL.Server 1.0.7
github Horusiath/fsharp.core.extensions:0ff5753bb6f232e0ef3c446ddcc72345b74174ca
DataLoader.fsx
#load ".paket/load/net50/FSharp.Data.GraphQL.Server.fsx"
#load "paket-files/Horusiath/fsharp.core.extensions/src/FSharp.Core.Extensions/Prolog.fs"
#load "paket-files/Horusiath/fsharp.core.extensions/src/FSharp.Core.Extensions/AsyncExtensions.fs"
type Person =
{
ID : string
Name : string
}
// Mocks a real database access layer
module DB =
// Used to avoid interleaving of printfn calls during async execution
let private logger = MailboxProcessor.Start (fun inbox -> async {
while true do
let! message = inbox.Receive()
printfn "DB: %s" message
})
let private log x =
logger.Post(x)
// Our data-set
let private people =
[
{ ID = "alice"; Name = "Alice" }, [ "bob"; "charlie"; "david"; "fred" ]
{ ID = "bob"; Name = "Bob" }, [ "charlie"; "david"; "emily" ]
{ ID = "charlie"; Name = "Charlie" }, [ "david" ]
{ ID = "david"; Name = "David" }, [ "emily"; "fred" ]
{ ID = "emily"; Name = "Emily" }, [ "fred" ]
{ ID = "fred"; Name = "Fred" }, []
]
|> Seq.map (fun (p, fs) -> p.ID, (p, fs))
|> Map.ofSeq
let fetchPerson id =
async {
log $"fetchPerson {id}"
match people |> Map.find id with
| (x, _) -> return x
}
let fetchPersonBatch ids =
async {
let idsString = String.concat "; " ids
log $"fetchPersonBatch [ {idsString} ]"
return
people
|> Map.filter (fun k _ -> Set.contains k ids)
|> Map.toSeq
|> Seq.map (snd >> fst)
|> Seq.toList
}
let fetchFollowers id =
async {
log $"fetchFollowers {id}"
match people |> Map.tryFind id with
| Some (_, followerIDs) -> return followerIDs
| _ -> return []
}
// GraphQL type definitions
open FSharp.Core
open FSharp.Data.GraphQL
open FSharp.Data.GraphQL.Types
#nowarn "40"
[<NoComparison>]
type Root =
{
FetchPerson : string -> Async<Person>
FetchFollowers : string -> Async<string list>
}
let rec personType =
Define.Object(
"Person",
fun () -> [
Define.Field("id", ID, fun ctx p -> p.ID)
Define.Field("name", String, fun ctx p -> p.Name)
Define.AsyncField("followers", ListOf personType, fun ctx p -> async {
let root = ctx.Context.RootValue :?> Root
let! followerIDs = root.FetchFollowers p.ID
let! followers =
followerIDs
|> List.map root.FetchPerson
|> Async.Parallel
return Seq.toList followers
})
])
let queryRoot = Define.Object("Query", [
Define.AsyncField(
"person",
personType,
"Fetches a person by ID",
[
Define.Input("id", ID)
],
fun ctx root -> async {
let id = ctx.Arg("id")
return! root.FetchPerson id
})
])
// Construct the schema once to cache it
let schema = Schema(queryRoot)
// Run an example query...
// Here we fetch the followers of the followers of the followers of `alice`
// This query offers many optimization opportunities to the data-loader
let query = """
query Example {
person(id: "alice") {
id
name
followers {
id
name
followers {
id
name
followers {
id
name
}
}
}
}
}
"""
let executor = Executor(schema)
async {
// Construct a data-loader for fetch person requests
let fetchPersonBatchFn (requests : Set<string>) =
async {
let! people =
requests
|> DB.fetchPersonBatch
let responses =
Seq.zip requests people
|> Map.ofSeq
return responses
}
let fetchPersonContext = DataLoader.context ()
let fetchPersonLoader = DataLoader.create fetchPersonContext fetchPersonBatchFn
// Construct a data-loader for fetch follower requests
let fetchFollowersBatchFn (requests : Set<string>) =
async {
let! responses =
requests
|> Seq.map (fun id ->
async {
let! followerIDs = DB.fetchFollowers id
return id, followerIDs
})
|> Async.Parallel
return Map.ofSeq responses
}
let fetchFollowersContext = DataLoader.context ()
let fetchFollowersLoader =
DataLoader.create fetchFollowersContext fetchFollowersBatchFn
let root =
{
FetchPerson = fun id -> fetchPersonLoader.GetAsync(id)
FetchFollowers = fun id -> fetchFollowersLoader.GetAsync(id)
}
// Uncomment this to see how sub-optimal the query is without the data-loader
// let root =
// {
// FetchPerson = DB.fetchPerson
// FetchFollowers = DB.fetchFollowers
// }
// See https://bartoszsypytkowski.com/data-loaders/
do! Async.SwitchToContext fetchPersonContext
do! Async.SwitchToContext fetchFollowersContext
// Execute the query
let! response = executor.AsyncExecute(query, root)
printfn "%A" response
}
|> Async.RunSynchronously
关于.net-core - 如何在 FSharp.Data.GraphQL 中实现批处理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63685864/
我已尽力在 Google 中找到答案。但我认为这与我的情况不符。 请给我一个指南。非常感谢。 如果您是我,您可以卸载 + 安装 Visual Emprise。 这个原因让我想了很多次。 感谢所有在我遇
给定一个具有 FSharp 样式函数的接口(interface)。 type IUseless = abstract member Listify: string -> int -> stri
我正在尝试将 F# 用于 ASP.NET MVC 应用程序。我的 Controller 操作将 F# 列表发送到 View ,因此我写道: >" %> 当然,要使其正常工作,我必须将 Microsof
这两个文件似乎与 FSharp.Core.dll 一起位于 F# 的目录中。 如果忘记将它们与 FSharp.Core.dll 放在一起,这两个文件似乎一直是麻烦的根源。 它们似乎是二进制文件,所以我
我很困惑。 我正在升级一个旧项目,它到处都引用了 FSharp.Core 4.4,但 nuget 上 fsharp.core 的最新版本是 4.1.*.* 是否有文件解释版本号倒退的情况? 最佳答案
我在发布 Visual Studio C# Web 服务时遇到问题。构建良好,运行良好,但在发布时出现这个奇怪的错误: Copying file any\netstandard1.6\FSharp.C
此问题基于本类(class)第一周的函数式随机生成器:https://www.coursera.org/course/reactive 该类(class)基于 Scala,我正尝试在 FSharp 中
我需要为我的 F Sharp 记录修改和添加新属性。但随后它会为没有这个新字段的先前实例提供错误。我把它设置为 Nullable ,但仍然出现同样的错误,请帮我解决这个问题 最佳答案 我认为您的意思是
我刚刚真正进入 FSharp 并且我已经为此苦苦挣扎了很长一段时间,因为我不明白为什么我没有在比赛声明中得到预期的结果。 根据消息编号,我需要将数组(拆分)中的某些值与我想要返回的记录上的字段相匹配。
假设我正在使用如下列表: let items = [ Some(1); None; Some(8); ];; 只有Some的最短方法是什么?列表中的值? items |> List.filter Op
似乎必须覆盖 Equality 才能覆盖比较。 这是真的吗?我失踪有什么原因吗? 最佳答案 不。可以只进行自定义比较 [] [] type Node = | Data of string
由于 FSharp 中具有多个参数的函数会固有地变成只有一个参数的函数,Seq.filter 的签名应该不得不 Seq.filter predicate source ? 会有多不同 Seq.filt
是否等效/更好地工作 与 Event module在 Event type 或在 publish 上使用 Observable事件的属性 从功能上看,它似乎是等效的,我想区别在于“语义”: 我们是否在
我正在研究具有多个状态的 F# 代理,即使用“let rec/and”关键字组合(根据 Expert F# 3.0 的“消息处理和状态机”)来提供多个异步 block 。到目前为止我能找到的唯一例子是
我正在尝试使用 F# 生成随机三元组列表 -> 两个随机数及其总和: let foo minNum maxNum = let rnd = System.Random() let fir
我想以特定方式格式化元组,并且我尝试通过检查元组的类型(2 个元素、3 个元素等)来实现此目的。我在第三行收到错误消息: This runtime coercion of type test from
以下表达式是否会使用 fsharp 编译器生成任何代码? let xs = List.rev <| List.rev xs 最佳答案 不,事实并非如此。您可以查看 sharplab 上的输出 IL验证
这个问题已经有答案了: Ranges A to B where A > B in F# (5 个回答) 已关闭10 年前。 有没有一种方法可以按降序排列范围? 例如 [1..4] 评估为 > val
我想以特定方式格式化元组,并且我尝试通过检查元组的类型(2 个元素、3 个元素等)来实现此目的。我在第三行收到错误消息: This runtime coercion of type test from
以下表达式是否会使用 fsharp 编译器生成任何代码? let xs = List.rev <| List.rev xs 最佳答案 不,事实并非如此。您可以查看 sharplab 上的输出 IL验证
我是一名优秀的程序员,十分优秀!