Using ConfigurationManger in F# project - f#

I am simply trying to use the ConfigurationManager within an F# project but I am getting the error:
This value is not a function and cannot be applied
here is the code:
open System.Configuration
let connectionString = ConfigurationManager.ConnectionStrings["ManagementDb"].ConnectionString
I have also referenced the System.Configuration library.

I haven't verified this in Visual Studio, but usually there is a dot ('.') when using an F# index.
i.e.
let connectionString = ConfigurationManager.ConnectionStrings.["ManagementDb"].ConnectionString

Related

Let ParseAndCheckFileInProject recognize symbols from Nuget dependecies

When using FSharp.Compiler.SourceCodeServices.FSharpChecker.ParseAndCheckFileInProject from FSharp Compiler Service for whole-project analysis, how can NuGet dependencies be included for symbolic resolution?
In the project under analysis, some project file (*.fsproj) contains some <PackageReference> element, e.g. <PackageReference Include="NodaTime" Version="3.0.3" />, and some source file contains an open statement open NodaTime and somewhere some symbol DateInterval refering to NodaTime.DateInterval. For me, the FSharp Compiler Service seems to fail to resolve DateInterval to NodaTime.DateInterval in that source file.
What I am currently doing is, summarized:
let checker = FSharpChecker.Create()
let options: FSharpProjectOptions = { ... }
checker.ParseAndCheckFileInProject (...)
Given that I use ParseAndCheckFileInProject, what is necessary for checker and/or options to consider symbols coming from NuGet dependencies?
I think you have to extract the FSharpProjectOptions from the .fsproj project file. For old-style .NET Framework projects, there used to be a method called ProjectCracker.GetProjectOptionsFromProjectFile that would do this, but it has been replaced with Dotnet.ProjInfo, which also supports .NET Core. See this SO question for details.

Can I create a conditional literal?

In order to create a Json provider I need to pass a literal with the path. There are several people working on the project from different locations, and the paths are different in each case. (Actually only the beginning of each path). I tried to create a literal with pattern matching but the compiler does not accept it. Is there another way to do this?
My failed attempt is below:
open FSharp.Data
[<Literal>]
let bitbucketRoot = // Error message: This is not a valid constant expression
let computerName = Environment.MachineName
match computerName with
| "DESKTOP-G3OF32U" -> "C:\\Users\\Fernando"
| "HPW8" -> #"H:\Dropbox\"
| _ -> failwith "Unknown computer"
[<Literal>] // Error message: This is not a valid constant expression
let projDataPath = bitbucketRoot + #"Bitbucket\VSProjects\Fractal10\Fractal10\data\"
[<Literal>] // Error message: This is not a valid constant expression
let jsonPath = projDataPath + "fractal.json"
type PathInfo = JsonProvider<Sample=jsonPath>
I would advise that you store it in source control and make it a path relative to your project root, assuming you are working out of a common source control repository.
Either that, or host the sample on a public URL. (I wouldn't actually recommend this because including it in your source repository allows versioning and doesn't publicly expose your data)
You cannot create a conditional literal as the other comments point it out. However this is a fairly frequent use case and the way to deal with it is as follows:
#r #"..\packages\FSharp.Data\lib\net40\FSharp.Data.dll"
open FSharp.Data
open System
open System.IO
[<Literal>]
let JsonSource = __SOURCE_DIRECTORY__ + #"\test.json"
type JSonType = JsonProvider<JsonSource>
let json1 = JSonType.GetSamples()
let anotherPath = #"C:\tmp"
let anotherJson = anotherPath + #"\test.json"
let json2 = JSonType.Load(anotherJson)
The __SOURCE_DIRECTORY__ directive will point to the project root (just display it in the REPL) and then you can add the filename to it and make that a literal. If you check in this file into a git repo, then everyone who checks it out can have it in a relative path, and you can refer it when generating the type. When actually using the type or referring to the full file you can just use the .Load() method to load any file, and this doesn't have to be a literal.
There is actually a second way, which could work for you depending on the circumstances, compile a sample, and distribute it as a .dll. You can refer to this and use it directly without having access to the actual file. Please see the Using the JSON Provider in a Library section at the end of the documentation.
I have not tried referring to the json in a config file, it might also be possible.

Referencing external assembly fails

I can't get referencing a private assembly working. I've followed the documentation, but it still fails with the error message:
2016-09-29T19:43:08.615 startup(2,1): error FS82: Could not resolve this reference. Could not locate the assembly "Backend.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245)
Here is the run.fsx file:
#r "Backend.dll"
open System
open System.IO
open System.Net
open System.Net.Http.Headers
open System.Collections.Generic
open CoP
let createResponse json =
let responseJson = Request.handleJson json
let response = new HttpResponseMessage()
response.Content <- new StringContent(responseJson)
response.StatusCode <- HttpStatusCode.OK
response.Content.Headers.ContentType <- MediaTypeHeaderValue("application/json")
response
let Run (req: HttpRequestMessage) =
async {
let! json = req.Content.ReadAsStringAsync()
return createResponse json
} |> Async.StartAsTask
I've also placed the Backend.dll in a bin folder inside the same folder as the function.
What am I missing?
Looks like you ran into a bug with private assembly resolution in the Azure Functions F# implementation.
I've opened this issue for tracking and will have a fix included in the next release:
https://github.com/Azure/azure-webjobs-sdk-script/issues/733
In the meantime, you should be able to reference your private assembly by using:
#r "bin/Backend.dll"
Hope this helps!
If that was a question about .fsx scripts alone, I'd say you're missing the part where you tell FSI where to look for the dll to reference:
#I "bin"
#r "BackEnd.dll"
Is there anything Azure does to put the .\bin folder within the context reachable by #r directive?

The type 'XmlProvider' is not defined

I'm trying to use the FSharp.Data third party library but am getting an error The type 'XmlProvider' is not defined on the XmlProvider class.
namespace KMyMoney
open FSharp.Data
module Read =
let xml = File.ReadAllText("KMyMoneySampleFile.xml")
type KMyMoneySource = XmlProvider<xml>
I'm using NuGet to get the library. Library is 'FSharp.Data 1.1.8'
When I type FSharp.Data. There are four options given: Csv, FreebaseOperators, Json, and RuntimeImplementation.
Am I missing something? I'm relatively new to F#. So, sorry for the simple question. I've looked on GitHub but haven't seen any mention of this problem. I am creating a library in F#.
The parameter between <> is the Sample parameter of the type provider, which has to be a compile time constant. That sample is used to infer the structure of the xml.
Try this instead:
namespace KMyMoney
open FSharp.Data
module Read =
type KMyMoneySource = XmlProvider<"KMyMoneySampleFile.xml">
and then do
let xml = KMyMoneySource.Load("KMyMoneySampleFile.xml")
or if you're reading the same file you used as the XmlProvider sample parameter, just do this:
let xml = KMyMoneySource.GetSample()
Note that Type Providers are a feature of F# 3.0, so this only works in VS2012 or upper. If you're using VS2010, you'll just get a bunch of syntax errors.
The data has to be available at compile-time which is achieved by putting a file reference in the angle brackets like this (notice that it is a string literal containing a file path, not a string binding containing the data). You can also achieve this by putting a string literal containing the format in the brackets:
type Stocks = CsvProvider<"../docs/MSFT.csv">
let csv = new CsvProvider<"1,2,3", HasHeaders = false, Schema = "Duration (float<second>),foo,float option">()
See here for more information.
Check out this link. Basically you need to add System.Xml.Linq.dll also as reference to your project.

How is one supposed to use the F# SqlDataConnection TypeProvider with an App.Config file?

I am using the type expression:
type dbSchema = SqlDataConnection<ConnectionStringName="X1", ConfigFile="App.config">
This works great at compile time (I have full access to all the db types), but it fails at run time. I presume it's because the config file generated in the console application's bin directory is named something else, such as MyAppName.exe.config, and therefore the App.config file is not found.
Certainly, for an ASP.NET MVC type app that uses web.config, there's no issue because the compile and runtime config filenames are the same.
Fortunately, placing a duplicate App.config in the bin directory does remediate the problem, but is that what we are expected to do? Any thoughts?
The description of how the type provider definition works is misleading - the value in the typedef really only matters at code/compile time, and as a default at runtime. However, as you've noted, it isn't very smart about finding the correct config file at runtime.
You can accomplish what you want by passing the connection string as a parameter to GetDataContext:
type dbSchema = SqlDataConnection<ConnectionStringName="X2">
let db = dbSchema.GetDataContext(ConfigurationManager.ConnectionStrings.["X2"].ConnectionString)
...or if you also want to make it work in F# interactive, wrap it like so:
type dbSchema = SqlDataConnection<ConnectionStringName="X2">
#if COMPILED
let db = dbSchema.GetDataContext(ConfigurationManager.ConnectionStrings.["X2"].ConnectionString)
#else
let db = dbSchema.GetDataContext()
#endif
(Note that you will need a reference to System.Configuration.)
I don't have a VS2012 on this PC but this should be what you're looking for :
let exeConfigFile = Path.GetFileName(System.Reflection.Assembly.GetEntryAssembly().Location) + ".config"
let defaultConfigFile = "App.config"
let configFile = if File.Exists(exeConfigFile) then exeConfigFile else defaultConfigFile
type dbSchema = SqlDataConnection<ConnectionStringName="X1", ConfigFile=configFile>

Resources