Search code examples
azuref#type-providersf#-interactive

SqlDataProvider connection string in Suave on Azure


I can't get SqlDataProvider to work when executed in a fsx script which is running in an Azure Web Site.

I have started from the samples that Tomas Petrecek has here: https://github.com/tpetricek/Dojo-Suave-FsHome.

In short it is a FSX script that is executed using the IIS httpPlatformHandler so that all http requests to my Azure Web site is forwarded to my F# script.

The F# Script use Suave to handle the requests.

When I tried adding some database access to my HTTP handlers I got into problems.

The problematic code looks like this:

[<Literal>]
let connStr = "Server=(localdb)\\v11.0;Initial Catalog=My_Database;Integrated Security=true;" 
[<Literal>]
let resolutionFolder = __SOURCE_DIRECTORY__
FSharp.Data.Sql.Common.QueryEvents.SqlQueryEvent |> Event.add (printfn "Executing SQL: %s")

// the following line fails when executing in azure
type db = SqlDataProvider<connStr, Common.DatabaseProviderTypes.MSSQLSERVER, ResolutionPath = resolutionFolder>

let saveData someDataToSave =
  let ctx = db.GetDataContext(Environment.GetEnvironmentVariable("SQLAZURECONNSTR_QUERIES"))
  .....
  /// code using the context here

This works just fine when I run it locally, but when I deploy it to the azure site it will fail at the line where the type dbis created.

The error message is (line 70 is the line that has the type db = ...:

D:\home\site\wwwroot\app.fsx(70,11): error FS3033: The type provider 'FSharp.Data.Sql.SqlTypeProvider' reported an error: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 52 - Unable to locate a Local Database Runtime installation. Verify that SQL Server Express is properly installed and that the Local Database Runtime feature is enabled.)

The design-time database in the connStr is not available in the azure site, but I thought this is why we have the GetDataContext overload that takes a connection string to be used at run-time?

Is it because it is running as a script and not as compiled code that it is trying to access the database when creating the TypeProvider? If yes, does it mean that my only option is to compile and provide the database code as a compiled assembly that I load and use in my Suave FSX script?

Reading the connection string from a config file does not work very well as this is in a azure site. I really need to get the connection string from an environment variable (which is set in the azure management interface).


Solution

  • Hmm, this is a bit unfortunate - as @Fyodor mentioned in the comments, the problem is that the script-based deployment to Azure actually compiles the script on the Azure machine - and so you need to have a statically-resolved connection string that works on Azure.

    There are two options:

    1. Use compiled project instead. If you compile your F# code locally and deploy the compiled code to Azure it will work. Sadly, there are no good samples for that.

    2. Do some clever trick to make the connection string accessible to the script at compile time.

    3. Send a PR to the SQL provider so that you can give it the name of an environment variable and it reads the connection string from there.

    I think (3) would actually be quite nice and useful feature.

    I'm not necessarily sure what the best way to do (2) would be. But I think you might be able to modify app.azure.fsx so that it creates a file (say connection.fsx) that contains something like:

    module Connection
    let [<Literal>] ConnString = "<Contents of SQLAZURECONNSTR_QUERIES>"
    

    Then app.fsx could load this script and use Connection.ConnString in the argument of SQL type provider.