代码之家  ›  专栏  ›  技术社区  ›  Franco Tiveron

自定义f querybuilder

  •  1
  • Franco Tiveron  · 技术社区  · 7 年前

    如果我像(f interactive)中那样创建自定义查询生成器

    type T1 = T1 of int list
        with
            member this.Content = let (T1 list) = this in list
    
    type QueryBuilder1() = 
        inherit Linq.QueryBuilder()
            member __.Source (source: T1) = base.Source(source.Content)
    
    let qb1 = new QueryBuilder1()
    
    let list = [1;2;3;4;5]
    let t1 = T1 list
    
    let q1 = query { for i in list do select i }
    let q2 = query { for i in t1.Content do select i }
    let q3 = qb1 { for i in t1 do select i } // OK
    

    一切正常。

    但是如果我在SQL数据库源上尝试同样的方法

    #r @"C:\Root\Project\Ocnarf\packages\SQLProvider.1.1.44\lib\net451\FSharp.Data.SqlProvider.dll"
    
    let [<Literal>] dbVendor = FSharp.Data.Sql.Common.DatabaseProviderTypes.MSSQLSERVER
    let [<Literal>] schemaConnString = @"Data Source=..."
    type internal Schema = FSharp.Data.Sql.SqlDataProvider<dbVendor, schemaConnString>
    type DowntimeEntity = Schema.dataContext.``dbo.DowntimesEntity``
    type DowntimeQuery = System.Linq.IQueryable<DowntimeEntity>
    
    type T2 = T2 of DowntimeQuery
        with
            member this.Query = let (T2 q) = this in q
    
    type QueryBuilder2() = 
        inherit Linq.QueryBuilder()
            member __.Source (source: T2) = base.Source(source.Query)
    
    let db = Schema.GetDataContext()
    let tables = db.Dbo
    let qry = tables.Downtimes
    let t2 = T2 qry
    let qb2 = new QueryBuilder2()
    
    let q4 = query { for d in qry do select d }
    let q5 = query { for d in t2.Query do select d }
    let q6 = qb2 { for d in t2 do select d } // exception
    

    然后我得到以下运行时异常

    System.NotSupportedException:这不是有效的查询表达式。 方法 'microsoft.fsharp.linq.querysource 2[FSharp.Data.Sql.Common.SqlEntity,System.Linq.IQueryable] Source[IQueryable](T2)' was used in a query but is not recognized by the F#-to-LINQ query translator. Check the specification of permitted queries and consider moving some of the operations out of the query expression at Microsoft.FSharp.Linq.QueryModule.TransInner$cont@1180-3(Boolean check, FSharpExpr immutQuery, Unit unitVar) at Microsoft.FSharp.Linq.QueryModule.TransInner(CanEliminate canElim, Boolean check, FSharpExpr immutQuery) at Microsoft.FSharp.Linq.QueryModule.TransInner(CanEliminate canElim, Boolean check, FSharpExpr immutQuery) at Microsoft.FSharp.Linq.QueryModule.TransInnerApplicative(Boolean check, FSharpExpr source, FSharpVar immutConsumingVar, FSharpExpr immutConsumingExpr) at Microsoft.FSharp.Linq.QueryModule.TransInner(CanEliminate canElim, Boolean check, FSharpExpr immutQuery) at Microsoft.FSharp.Linq.QueryModule.TransInnerAndCommit(CanEliminate canElim, Boolean check, FSharpExpr x) at Microsoft.FSharp.Linq.QueryModule.TransInnerWithFinalConsume(CanEliminate canElim, FSharpExpr immutSource) at Microsoft.FSharp.Linq.QueryModule.EvalNonNestedInner(CanEliminate canElim, FSharpExpr queryProducingSequence) at Microsoft.FSharp.Linq.QueryModule.EvalNonNestedOuter(CanEliminate canElim, FSharpExpr tm) at Microsoft.FSharp.Linq.QueryModule.clo@1727-1.Microsoft-FSharp-Linq-ForwardDeclarations-IQueryMethods-Execute[a,b](FSharpExpr 一 q)在$fsi_0005.main@()

    问题

    我应该做什么来修复这个异常?

    1 回复  |  直到 7 年前
        1
  •  1
  •   Tomas Petricek    7 年前

    Source

    Run

    <inst>.Source(<arg>) <inst>.Source(<arg>.Query)

    open Microsoft.FSharp.Quotations
    
    let rec replace expr = 
      match expr with 
      | Patterns.Call(Some inst, mi, [arg]) when mi.Name = "Source" -> 
          let sourceMethod =
            typeof<Linq.QueryBuilder>.GetMethods() 
            |> Seq.filter (fun mi -> 
                 mi.Name = "Source" && 
                 mi.GetParameters().[0].ParameterType.Name = "IQueryable`1")
            |> Seq.head
          let sourceMethod = 
            sourceMethod.MakeGenericMethod 
              [| typeof<DowntimeEntity>; typeof<System.Linq.IQueryable> |]
          let queryProp = typeof<T2>.GetProperty("Query")
          Expr.Call(inst, sourceMethod, [ Expr.PropertyGet(arg, queryProp) ])
      | ExprShape.ShapeCombination(o, args) -> 
          ExprShape.RebuildShapeCombination(o, List.map replace args)
      | ExprShape.ShapeLambda(a, b) -> Expr.Lambda(a, replace b)
      | ExprShape.ShapeVar(v) -> Expr.Var(v)
    
    type QueryBuilder2() = 
        inherit Linq.QueryBuilder()
        member __.Source (source: T2) = base.Source(source.Query)
        member __.Run(e:Expr<Linq.QuerySource<'a, System.Linq.IQueryable>>) = 
            let e = Expr.Cast<Linq.QuerySource<'a, System.Linq.IQueryable>>(replace e.Raw)
            base.Run(e) 
    

    repalce replace