0
votes

I have the following code to get a row from SQL database. It returns either a record or None. However, it doesn't return because getLogin has the type of unit = () and there is a warning

warning FS0020: This expression should have type 'unit', but has type 'login option'. Use 'ignore' to discard the result of the expression, or 'let' to bind the result to a name.

How to resolve the issue? Is it the best way to handle Ado.Net?

type login = { userName: string; password: string; downloadDir: string}

let getLogin =
    let conn = new SqlConnection("Data Source=......;Integrated Security=SSPI;")

    try
        conn.Open()

        let cmd = new SqlCommand("select * FROM Login where website = 'xxx'", conn)
        cmd.CommandType <- CommandType.Text
        use reader = cmd.ExecuteReader() 
        if (reader.HasRows) then (
            reader.Read() |> ignore
            let userName = reader.GetString(5)
            let password = reader.GetString(6)
            printfn "%s" userName
            Some { userName = userName; password = password; downloadDir = "zzz" }
        )
        else (
            printfn "Cannot find login information."
            None
        )

        conn.Close()
    with
        | :? System.Data.SqlClient.SqlException as sqlEx -> printfn "%A" sqlEx.Message
        | _ -> printfn "Unknown Exception"

Update: I've updated the code by Thomas' suggestion.

let getLogin =
    use conn = new SqlConnection("Data Source=mdyptcafgprd101;Initial Catalog=App;Integrated Security=SSPI;")

    try
        conn.Open()

        let cmd = new SqlCommand("select * FROM app.WebCrawler.LoginLbl where website = 'CTSLink'", conn)
        cmd.CommandType <- CommandType.Text
        use reader = cmd.ExecuteReader() 
        if (reader.HasRows) then (
            reader.Read() |> ignore
            let userName = reader.GetString(5)
            let password = reader.GetString(6)
            Some { userName = userName; password = password; downloadDir = "zzz" }
        )
        else (
            printfn "Cannot find login information."
            None
        )

    with
        | :? System.Data.SqlClient.SqlException as sqlEx -> printfn "%A" sqlEx.Message
        | _ -> printfn "Unknown Exception"

However, now it gets the following error on line | :? System....

s.fs(31,69): error FS0001: Type mismatch. Expecting a
    'a -> login option    
but given a
    'a -> unit    
The type 'login option' does not match the type 'unit'
> 
1

1 Answers

2
votes

The problem is that your if expression is followed by another line:

if reader.HasRows then
  (...)
else
  (...)
conn.Close()

... and so the compiler thinks that you are ignoring the result of if and instead returning the result of the last expression, which is conn.Close().

The easiest way to fix this is to use the use construct - it automatically disposes of the conn value when it comes out of scope, and so you do not need the last line and can just use if as the last expression in your code:

use conn = (...)
if reader.HasRows then
  (...)
else
  (...)

Also, you'll need to return some result in the with ... branch - that handles an exception and you either need to raise another exception or return a valid value - in this case, probably None.