fsprojects / FSharp.Linq.ComposableQuery

Compositional Query Framework for F# Queries, based on "A Practical Theory of Language-Integrated Query"
http://fsprojects.github.io/FSharp.Linq.ComposableQuery/
MIT License
67 stars 13 forks source link

queries over data provided by ODataService type providers #6

Closed jamescheney closed 10 years ago

jamescheney commented 10 years ago

Queries

For example, the following simple query over the Northwind database provided by ODataService fails. (Additional examples are in the new file NorthwindTests.)

open Microsoft.FSharp.Data.TypeProviders;

type Northwind = ODataService<"http://services.odata.org/Northwind/Northwind.svc">
let db = Northwind.GetDataContext()

// Some tests to compare 

let dbQuery =  FSharpComposableQuery.TopLevelValues.query

dbQuery { for x in db.Customers do yield x }

The error message is:

System.Exception: Unexpected table reference PropertyGet (Some (PropertyGet (None, db, [])), Customers, []) System.Data.Services.Client.DataServiceQuery`1[UnitTestProject1+Northwind+ServiceTypes+Customer]
Result StackTrace:  
at FSharpComposableQuery.QueryImpl.from@464-2.Invoke(String message)
   at Microsoft.FSharp.Core.PrintfImpl.go@523-3[b,c,d](String fmt, Int32 len, FSharpFunc`2 outputChar, FSharpFunc`2 outa, b os, FSharpFunc`2 finalize, FSharpList`1 args, Int32 i)
   at Microsoft.FSharp.Core.PrintfImpl.run@521[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args)
   at Microsoft.FSharp.Core.PrintfImpl.capture@540[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args, Type ty, Int32 i)
   at .$Reflect.Invoke@720-4.Invoke(T1 inp)
   at FSharpComposableQuery.QueryImpl.handleSpecificCall@488(QueryBuilder this, FSharpOption`1 obj, MethodInfo func, FSharpList`1 args, Type expr_ty)
   at FSharpComposableQuery.QueryImpl.handleSpecificCall@494-2.Invoke(Type ty, Type _arg4)
   at FSharpComposableQuery.QueryImpl.QueryBuilder.Norm[T](FSharpExpr`1 expr)
   at FSharpComposableQuery.QueryImpl.QueryBuilder.Run[T](FSharpExpr`1 q)
jamescheney commented 10 years ago

This exception is being raised by the following code in Query.fs:

| Patterns.PropertyGet(Some(Variable(None, _db)), _tbl, []) as e -> 
                    match e.Type with
                    | QuerySourceTy(ty, _) -> Table(e, ty)  // assume it's a db table ref
                    | TableTy ty -> Table(e, ty)            // assume it's a db table ref
                    | ty' -> failwithf "Unexpected table reference %A %A" e ty'

It looks like we need to recognize [System.Data.Services.Client.DataServiceQuery`1[UnitTestProject1+Northwind+ServiceTypes+Customer]] as an allowed table type. Is there a good way of recognizing all of the possible table types?

jamescheney commented 10 years ago

Now fixed.

ixtreon commented 10 years ago

Although the fix addresses the specific issue, I believe it is highly specific to the System.Data.Services.Client.DataServiceQuery<T> type.

First off, the exception occurs as the only variable references valid in a query are those querying a database table, and the DataServiceQuery<T> type is not recognized as such.

Before this issue was posted the code had checked if a variable was of type Microsoft.Fsharp.Linq.QuerySource<T,Q> or System.Data.Linq.Table<T> to determine if it is a table, and commit a0b17898 added an explicit check to test if a variable is of type DataServiceQuery<T> (along with an extra reference for the library).

I observed that both DataServiceQuery<T> and Table<T> implement the System.Linq.IQueryable<T> interface which is also the base unwrapped type all query methods work with. On the other hand, the Microsoft.Fsharp.Linq.QuerySource<T,Q> type is the wrapped type they use.

I thus believe checking for those two types, including for subtypes of the former, would be both correct and sufficient.