F# Type Initialization Exception with Async Parallel iteration

135 Views Asked by At

I am getting the error "Type Initialization Exception occurred in File.exe; the type initializer for File.Mod2 threw an exception." However the breakpoint on the exception is in Mod1--where incidentally there is nothing declared, nothing in the Locals window, nothing to debug... .

I thought it might have to do with whether I declare the type SqlDataConnection in one module or separately in each, or whether I assign db.GetDataContext globally in one module, in each module, in each function it's needed, or in one function and then pass it up/ around. But moving declarations around is not helping. (I got this error rarely in the past, and it kind of fixed itself. I forget, it was so rare, either I would hit continue. Or I would start over and not touch SQL Server while running.)

This code worked for weeks, able to run days at a time and upload 500M+ results. I'm always revising, but I don't think I touched anything in the Type Provider or database parts.

One final point: I can pinpoint exactly where in the iterations the exception is thrown after some detective work. As mentioned, the debugger breaks with the exception on the one line in Mod1. But if I walk through the iterations, the break follows upon the completion of the first ID in "IDList |> List.iter (fun ID -> GetDataLists ID |> IterateParams ID paramLists )", in Mod2. The break occurs after the last UpLoad result in Mod4, when the code should return to Mod2 for the next ID, but instead breaks in Mod1, at the first, most abstracted call. Finally, note that the error message says the exception was thrown in Mod2--even though the break is in Mod1. (And I can't step from Mod4 to Mod2 to examine the exception conditions--it jumps from Mod4 to Mod1.)

EDIT*** I got one detail wrong. The break does not jump from Mod4 to Mod1. Mod4 does return to Mod2. Flow returns to AsyncSeries in Mod2, from the last upload in Mod4 (which succeeds), if I step carefully. Then the next step from AsyncSeries in Mod2 is to the one line in Mod1.

Here is abstracted code just to try to illustrate the flow, with notes on the exception.

namespace MyNameSpace

module Mod4 =  //type provider is used here
    let UpLoadOneResult ID result = ... //****Last result of 1st ID uploads. 
                    //Exception then in Mod1--instead of iterating in Mod2. 
    let UploadResults ID resultsList = 
        resultsList |> List.iter (fun r -> UploadOneResult ID r)


module Mod3 = //type provider is used here
    let rec SolveResults ID kList mList rev resultList (x, y, z, w) = ...
        SolveResults ID kList.Tail mList.Tail newResultList (x, y, z, w)        


module Mod2 =
        type dbSchema1 = Microsoft.FSharp.Data.TypeProviders.
        SqlDataConnection<"Data Source=DESKTOP-55M3AJ5\SQLEXPRESS;
        Initial Catalog=Data;Integrated Security=True;
        MultipleActiveResultSets=True;">   
        //I omit this from example modules above, but they do have 
        //dbSchema2, etc. 

    let GetParamLists = ...  //this func just hard coded. my user interface for now.
        (xList, yList, zList, wList)

    let GetDataLists ID = 
        let db1 = dbSchema1.GetDataContext()    // "use"? "new"? () as argument? 
        let kList = query { for row in db1.Data ... } |> Seq.toList 
        let mList = query { for row in db1.Data ... } |> Seq.toList 
        (kList, mList)

    let IterateParams ID (xList, yList, zList, wList) (kList, mList) =   

        let MakeParamCombos xList yList zList wList =
            [for x in xList do
             for y in yList do 
             for z in zList do
             for w in wList do
             yield (x, y, z, w)]

        let AsyncSeries (x, y, z, w) = async { 
            return  SolveResults ID kList mList [] (x, y, z, w)        
            |> UpLdResults ID   }

        MakeParamCombos xList yList zList wList
        |> List.map AsyncSeries
        |> Async.Parallel 
        |> Async.RunSynchronously
        |> ignore


    let CalcResults  =   
        let db1 = dbSchema1.GetDataContext()
        let paramLists = GetParamLists
        let IDList = query { for row in db1.Data ... } |> Seq.toList
        IDList |> List.iter (fun ID -> GetDataLists ID |> IterateParams ID paramLists ) 
        //*** the first ID run completes. Code fails to return here for 2nd ID. 
        ()


module Mod1 =
    [<EntryPoint>] 
    let main argv =
        CalcResults |> ignore    //**ERROR point, on second iteration of "IDList |> List.iter ... "
        0
1

There are 1 best solutions below

1
RomnieEE On

It looks like the problem is simply that in one of my new edits I don't check for an empty list before taking List.Head in a place where I get an empty list with ID 2 (after ID 1...). So this question was a bit of a wild goose chase. The error was trivial. But it's not (just) that I'm entirely an idiot: In my processes for whatever reason, this elementary error caused an inscrutable exception and exception environment. And: it's not just that it never occurred to me to step through the code. Rather, the error would jump ahead of me when trying to step through--unless I put a break point on every single line in the code, and then stepped through it with F11.

I should probably delete this whole mess. I'll wait a few hours and look for a sign re that.

Related Questions in F#