【问题标题】:Constructing computation expression programmatically以编程方式构造计算表达式
【发布时间】:2016-08-17 12:28:47
【问题描述】:

我想从提供的数据结构中创建一个等效于query { ... } 计算表达式的方法(将FSharp.Data.TypeProviders 与LINQ2SQL 或LINQ2Entities 一起使用),即:

Collection("customers", [ 
    Field "customerId"; 
    Collection("orders", [ 
        Field "orderId" ]) ])

为此,我首先需要了解查询是如何转换为代码的。以以下查询为例:

query {
    for c in db.Customers do
    select (c.CustomerID, query {
        for o in c.Orders do
        select o.OrderID
    } |> Seq.toList)
} 

没有计算表达式语法会是什么样子?

【问题讨论】:

  • 计算表达式的一个很好的介绍是fsharpforfunandprofit.com/series/computation-expressions.html。每个关键字对应的内容在 MSDN 中有详细记录 - 例如,for ... in 转换为对构建器类型上的 For 函数的调用。要获得发出的确切代码,您始终可以使用反编译器。
  • 谢谢卢安。不幸的是,我在尝试使用query 表达式进行直接翻译时失败了。我也尝试阅读反编译的代码。然而,这个查询的主体结果是 typeof 和字节数的混合,直接写入一个数组并使用单一方法调用 - 可能该查询主体的一部分实际上是 F# 引用。你也不是特别适合人类阅读。
  • SQL Access 的类型提供程序有很多。自版本 3 起,F# 已经提供了类型提供程序 over EFL2S。但是..
  • 使用FSharp.Data.SqlClient's SqlCommandProvider 虽然您根本不需要映射。提供者将表公开为类型。将其视为类型安全、启用 LINQ 的 Dapper 等价物
  • @Panagiotis Kanavos - 如果您仔细阅读我的问题,您的所有问题都已经得到解答: 1. 我已经提到了我正在使用的特定 NuGet 包(FSharp.Data.TypeProviders 示例中带有 LinqToSql )。 2.我还需要返回具有嵌套一对多关系的实体(也在示例中给出)。所以 Dapper-like 提供者不会帮助我。在这里擦除类型提供程序也可能会失败。 3. 我明确提到我需要解构一个query(所有暴露query 表达式的类型提供程序都在SQL 数据库上工作)。

标签: f#


【解决方案1】:

我不建议以编程方式生成 F# 查询。

查询的主要目的是使编写漂亮的 F# 源代码成为可能。当它们被执行时,您编写的代码首先被转换为 LINQ 样式的表达式树,然后被转换为 SQL 查询。如果您设法将您的查询规范转换为 F# 查询,您会得到类似的结果:

 +------------+    +----------+    +----------------------+    +-----+
 | Your query | -> | F# query | -> | LINQ expression tree | -> | SQL |
 +------------+    +----------+    +----------------------+    +-----+

多次转换并没有错,但是有很多事情使通过 F# 查询的路径变得更加复杂:

  • 在您的查询格式中,事物似乎被表示为字符串。因此,您需要从字符串转到 .NET MethodInfo 才能返回字符串。

  • 1234563使用反射或类似的东西来访问它们。

TL;DR - 我认为直接获取查询表示并直接生成 SQL 会容易得多。转换的结构类似于生成 F# 查询,但您不必乱用引号和反射。

【讨论】:

  • 我也在考虑直接转换为 SQL,但有一个问题 - 我必须处理一对多和多对多关系并检测正确的外键。从 LINQ 的角度来看,列和连接都只是属性。
  • 另一种选择是从预定义的 F# 代码块组合查询,然后将它们组合在一起(请参阅stackoverflow.com/questions/10158512/…)。如果您的词汇量相当有限,那么这可能会更好。我的想法是基于您的样本中有字符串这一事实 - 这听起来非常通用。
【解决方案2】:

如果您想了解某物如何脱糖,只需将其放入报价单中即可。如果您使用示例执行此操作,您会看到

<@ 
    query {
        for c in db.Customers do
        select (c.CustomerID, query {
            for o in c.Orders do
            select o.OrderID
        } |> Seq.toList)
    } 
@>

大致相当于

query.Run 
    <@ query.Select(query.For(query.Source db.Customers, 
                              fun c -> query.Yield c), 
                    fun c -> 
                        c.CustomerID, 
                        query.Run <@ 
                                    query.Select(query.For(query.Source c.Orders, 
                                                           fun o -> query.Yield o), 
                                                 fun o -> o.OrderID) @> 
                             |> Seq.toList) @>

(我已经清理了一些无关变量)。我同意 Tomas 的观点,可能有比尝试直接创建这样的表达式更好的方法来实现您想要的。

【讨论】:

  • 整洁!但是您在哪里/如何查看报价产生的内容?
  • @DaxFohl - 我刚刚使用了 F# Interactive。您需要确保事先正确定义了所有标识符,并且输出不是超级可读的(例如,您会看到类似 PropertyGet (Some (o), OrderID, []) 而不是 o.OrderID 的东西),否则它是完全简单的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-20
  • 2014-06-12
  • 1970-01-01
相关资源
最近更新 更多