JordanMarr / SqlHydra

SqlHydra is a suite of NuGet packages for working with databases in F# including code generation tools and query expressions.
MIT License
223 stars 22 forks source link

Error after upgrading V2.5.0: 'Microsoft.FSharp.Core.FSharpOption`1 is not a F# record type. #92

Closed kgday closed 4 months ago

kgday commented 4 months ago

After upgrading to V2.5.0, a query that worked perfectly before upgrading is now giving me an error. I have another similar one in another part of my program also giving me an error.

The error is:

Type 'Microsoft.FSharp.Core.FSharpOption`1[[WGD.Data.public+ProductionOrder, WGD.DB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' is not an F# record type. (Parameter 'recordType')

The example code is below:

let private getDespatchDetails
        (ctx: QueryContext)
        (jsonOptions: JsonSerializerOptions)
        (despatchQuery: SelectQuery<int64>)
        =
        asyncResult {
            return!
                selectTask dbRead (Shared ctx) {
                    for line in ``public``.DespatchLine do
                        leftJoin po in ``public``.ProductionOrder on (line.JobID = Some po.Value.JobID)
                        where (line.DespatchID |=| subqueryMany despatchQuery)
                        orderBy line.DespatchID
                        thenBy line.LineNum
                        select (line, po)
                        mapArray (mapLineFromDb line po jsonOptions)
                }
                |> AsyncResult.ofTask
                |> AsyncResult.mapError (fun e -> $"Error occured getting despatch details. %s{e.Message}")
                |> AsyncResult.map (fun lines -> lines |> Array.groupBy (fun line -> line.DespatchID) |> Map.ofArray)
        }

I have had to revert back to verson 2.4.1 for now because I can't figure this out.

JordanMarr commented 4 months ago

Is there any Stack trace info you can provide?

kgday commented 4 months ago

Sorry, should have thought about providing that for you earlier:

System.ArgumentException: Type 'Microsoft.FSharp.Core.FSharpOption`1[[WGD.Data.public+ProductionOrder, WGD.DB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' is not an F# record type. (Parameter 'recordType')
   at Microsoft.FSharp.Reflection.Impl.checkRecordType(String argName, Type recordType, BindingFlags bindingFlags) in D:\a\_work\1\s\src\FSharp.Core\reflect.fs:line 1071
   at Microsoft.FSharp.Reflection.FSharpType.GetRecordFields(Type recordType, FSharpOption`1 bindingFlags) in D:\a\_work\1\s\src\FSharp.Core\reflect.fs:line 1220
   at SqlHydra.Query.SelectBuilders.queryWithSelectedColumns@123.Invoke(Query q, Selection _arg3)
   at Microsoft.FSharp.Collections.ListModule.Fold[T,TState](FSharpFunc`2 folder, TState state, FSharpList`1 list) in D:\a\_work\1\s\src\FSharp.Core\list.fs:line 295
   at SqlHydra.Query.SelectBuilders.SelectBuilder`2.Select[T](QuerySource`2 state, Expression`1 selectExpression)
   at WGD.Data.DespatchService.getDespatchDetails@267.Invoke(Unit unitVar) in D:\Development\Projects\Upwork\WGD\Src\WGD.DB\DespatchService.fs:line 267
   at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvoke[T,TResult](AsyncActivation`1 ctxt, TResult result1, FSharpFunc`2 part2) in D:\a\_work\1\s\src\FSharp.Core\async.fs:line 510
   at WGD.Data.DespatchService.getListForWhereAndPagingWith@318-7.Invoke(AsyncActivation`1 ctxt)
   at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in D:\a\_work\1\s\src\FSharp.Core\async.fs:line 112

Let me know if you also need the relevant schema types.

JordanMarr commented 4 months ago

Ok I see the problem. I can’t believe none of my tests covered this… but they will soon enough!

JordanMarr commented 4 months ago

SqlHydra.Query v2.5.1 is building and should be on NuGet shortly. I had to temporarily revert the smaller of the two changes in v2.5.0 where select (o, d) explicitly lists all columns rather than using SELECT *. That one was a very small performance enhancement, so definitely not a big deal to revert it until I can fix the bug.

Btw, you can now change this:

selectTask dbRead (Shared ctx) {
}

to this:

selectTask dbRead ctx {
}

The parameter now has an implicit conversion.

JordanMarr commented 4 months ago

Also, in your example, you should be able to use selectAsync instead of selectTask since you are using asyncResult. That way you wouldn't have to convert using |> AsyncResult.ofTask.

I believe there is also a taskResult CE NuGet package as well.

kgday commented 4 months ago

The reason I don't use selectAsync is the conversion to Async<Result<>>, catching any error. There are some quirks of fsToolkit.ErrorHandling.

AsyncResult.OfAsync expects a async<> but doesn't do a catch of any error, just wraps it in Ok.

AsyncResult.catch which I would like to use expects as input Async<Result<>> so it won't work either. Where as AsyncResyult.ofTask expects a Task<>, catches any error and gives me a AsyncResult<'a,'error> which I chain further.

kgday commented 4 months ago

And since this is part of a bolero application in a remote call, which requires async, I need to produce an async. Although, yes I could use TaskResult through out the function body and use Async.AwaitTask at the end and use AsyncTask.catch instead of AsyncResult.ofTask, since since that is basically what AsynResult.ofTask does.

JordanMarr commented 4 months ago

I'll leave this open until you are able to verify that it works in your environment.

kgday commented 4 months ago

Verified Ok. Thank you.