F#, WebSharper and Sql type Provider in action

I’m going to describe a real-world business requirement change in a web business app and how I can manage it with F# language and WebSharper platform.

I have an input and I want to make it multi-value

Well, I start from the code managing the DB, layer, where I have an Active Pattern based on the Selection type.

type ParsedTrade = NoSelection | IntSel of int

Where I will turn an int into an array of int:

type ParsedTrade = NoSelection | IntSel of int[]

Now the magic incantation starts with FSharp.Data.Sql Operators:

| IntSel i -> 
    query {
    for t in context.OilPhysical.EndurTradeValid do  
    join n_del in (!!) context.OilPhysical.EndurNominationValid on
             ((t.DealNumber,t.ParcelGroupId,t.ParcelId) 
             = (n_del.DeliveryDealNumber,n_del.DeliveryParcelGroup,n_del.DeliveryParcelId))
    join n_rec in (!!) context.OilPhysical.EndurNominationValid on
             ((t.DealNumber,t.ParcelGroupId,t.ParcelId) 
             = (n_rec.ReceiptDealNumber,n_rec.ReceiptParcelGroup,n_rec.ReceiptParcelId))
    join id_cost in (!!) context.OilPhysical.EndurProfitCenter on 
            (t.CostCenterId = id_cost.InternalId)
    join id_profit in (!!) context.OilPhysical.EndurProfitCenter on 
            (t.ProfitCenterId = id_profit.InternalId)
    where (t.BookingCompanyShortName = book 
        && ((isByTrade && t.DealNumber |=| i) || 
            (isByCargo && n_del.CargoId |=| i) || 
            (isByCargo && n_rec.CargoId |=| i) || 
            (isByCpty && t.ExternalLegalEntityId = (string i)) ))
    take 90
    select (t,n_del.CargoId, n_rec.CargoId, id_cost.EndurId, id_profit.EndurId)  
    }

Cool: the = becomes a |=| ! The Visual Studio 2019 IDE is guiding me and the language features are elegant, succinct and helpful.

End of the DB layer, now we are in WebSharper, server code, the remote API, and we have to adapt the Active Pattern for the array instead of a single int. How can I modify the following lines?

let (|Valid|_|) (str:string) =
    if String.IsNullOrWhiteSpace str then 
        Some(SqlDB.NoSelection) 
    else
        match Int32.TryParse str with
        | false, _ -> None
        | true, num ->
            if (num > 0) then Some(SqlDB.IntSel num) else None

My idea is to take advantage of the full functional programming power of F# here on the server side. So, I define my parser

let Int32TryParseArray (str:string) : int[] option =
    let parsed =
        str.Split(',')
        |> Array.map Int32.TryParse
    if parsed |> Array.exists(fun (ok,_) -> not ok) then None else
    parsed |> Array.map snd |> Some

and I use it in just one line!

let (|Valid|_|) (str:string) =
    if String.IsNullOrWhiteSpace str then 
        Some(SqlDB.NoSelection) 
    else
        Int32TryParseArray str |> Option.map SqlDB.IntSel

And it looks like the Server part is done! Let’s go for the Front-End, but thanks to the isomorphic WebSharper, we always see our friend F# also there! The technical requirement there is more complicated: I have to capture an other int from a string message in case of a certain alert type and pass it to the API in a comma separated format. Thanks to WebSharper I don’t have to deal with any JavaScript quirks.

The starting point, the code as it is before the change, is already very advanced, with F#  Anonymous Record Types needed to feed a jquery jtable… See the line that I will change

"AlertKey" => 
    {|
        title = "Key";
        list = true;
        sorting = true;
        edit = true;
        create = true;
        display = fun data -> 
            let param1 = {param_name = ServerModel.SelAlertKey; param_id = data?record?AlertKey}
            let param2 = {param_name = ServerModel.SelAlertType; param_id = alert2entity data?record?AlertCode}
            let post_form = {key_param_id = data?record?AlertKey; post_params = [| param1; param2|]}
            post_form  |==> EndPoint.Table 
            |> ToHtml
    |};

Into the new logic required for the multi-key enhancement:

let code : string = data?record?AlertCode
let msg : string = data?record?Message
let isMultiKey = code.StartsWith("A32") || code.StartsWith("A91")
let param1 = {param_name = ServerModel.SelAlertKey; param_id = 
    (if isMultiKey 
    then 
        data?record?AlertKey 
        + "," + 
        (msg.Split(' ') |> Array.last) 
    else data?record?AlertKey)
    }

Finally, I was showing a PL when the selection was a cargo number, therefore now I’ve to specify a when clause inside such a pattern matching

match selection with
| IntSel cargoIds when cargoIds.Length = 1 ->
    // .... showing the cargo PL
| _ -> 
    // ... as before

And you know… it will work at first compile!

One thought on “F#, WebSharper and Sql type Provider in action

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s