I have library using netstandard 2 and consuming it from a dotnet core 3.1 app and want to get some settings from settings.json or app.config. How do i do that in F#? What is the preferred way here?
I heard about
using using the configuration builder (like c#), it needs the package FsConfig
type Config = {
processId:int
}
let configurationRoot =
ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(“settings.json”).Build()
let result = appConfig.Get ()
this is somehow giving me this error
System.IO.FileLoadException: ‘Could not load file or assembly ‘TypeShape, Version=8.0.1.0, Culture=neutral, PublicKeyToken=null’. The located assembly’s manifest definition does not match the assembly reference. (0x80131040)’
using a typeprovider Fsharp.Configuration FSharp.Configuration this looks promising but in my dotnet core app it wouldnt work.
open FSharp.Configuration
type Settings = AppSettings<“app.config”>
Settings.FlagsFileSource
Thats what i am getting when i want to access the settings, last line above
System.IO.FileNotFoundException: ‘Could not load file or assembly ‘System.Configuration.ConfigurationManager, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51’. Das System kann die angegebene Datei nicht finden.’
I added this package “System.Configuration.ConfigurationManager,”
then i got this?
System.TypeLoadException: ‘Could not load type ‘System.Web.Hosting.HostingEnvironment’ from assembly ‘System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’.’
Any hints?
Looks like with the new alpha2 version of FSharp.Configuration its working.
I guess I have some settings in my library that i definetely need but wouldnt want to hard code.
My current solution is to use FSharp.Configuration in the latest alpha version. That works now. I have an app.config in my library project to satisfy the type provider and an app.config in my f# application to get the actual values from.
So for now that works. What i dont know is how i refresh the type provider once that has loaded some values. I posted this question here too.
Though I’ll give that Microsoft.Extensions.Configuration option another shot. Since I know that from my c# web project past.
Would you say that the type provider is the f#est way? Dont mind my asking I am a newbie in f#.
I’d say the “F#est way” is whatever gets the job done in a way that you (or your team) feel comfortable maintaining. Could be a type provider. Could be a 3rd-party library. All depends. But absent any other criteria to help “narrow the field”, just using whatever is in the .NET Standard Library is a safe default.
The following has nothing to do with F#, but is just a general observation regarding good API design.
If you read configuration values from a configuration system from within a library, you’ve now coupled the library to the configuration system. That coupling is implicit, because you can compile calling code which will fail at run time (if the configuration file isn’t present).
It makes it harder to use the library. It makes it harder to unit test the library.
A better option is to design a library’s API so that it takes a set of options as input. Then the caller can supply the configuration options read from whichever source it wants.
With this principle, you can’t compile the calling code if you don’t supply the configuration values.
Thanks for the hint. Thats actually what I’d like to do in the end. The thing is that I am still learning the concepts and coming from C# the config approach is more natural in the first place.
Can you point to me to a good example of your approach? Greatly appriciate that.
What I wrote also applies to C#, and most other languages that I’m aware of.
If, for example, you need an integer configuration value, design your functions so that they take an integer value as a parameter. In F#, it often pays to make the configuration values the leftmost parameters, because that means that callers can partially apply them.
If you need to pass around many configuration values, you can declare a type that encapsulates them.
If you can provide some example code that illustrates what you’re trying to do, I’ll be happy to give an example.
I’d like to know how what I did can be improved in regard of your comment @ploeh.
In my library I have an AppConfig module using the JSON type provider and defining functions to access the config values because the type is hidden in AppConfig.fsi. The application can then load the file and subsequently pass the object loaded by the AppConfig module to functions and types.
My library code is clearly coupled in the way you described. I experience the downside of the coupling because for running my tests I am now required to have a json configuration file. How would you fix this?
I don’t know that particular code base, but from glancing over it, it looks to me as though the part that’s causing you grief is a symptom of what’s almost always a fundamental problem in FP: impurity.
While you can load the configuration from disk with the load action, that’s the only option for creating a value of the type t (why is it called t, by the way?). That action is impure.
I would suggest that providing a pure function that can create a value of the the type t would make things much simpler.
thanks for your advice, I’ll take a shot at it.
This is a project I’ve been learning F# with, and (I hope) my FP approach improves over time. I think the t naming comes from OCaml code I’ve seen where the hidden type was called t.
I’m not sure if this forum is against necrobumping, but I came to the following conclusion after some time researching how to do it on ASP.NET (this code is using Giraffe, however it should be mostly the same for other frameworks):
Note that this is using the FsConfig package.
Declare the Configuration type and module somewhere.
open System.IO
open FsConfig
open Microsoft.Extensions.Configuration
type Configuration =
{ SuperSecret: string }
module Configuration =
let init =
let configurationRoot =
ConfigurationBuilder()
.AddEnvironmentVariables()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json")
.Build()
let appConfig = AppConfig(configurationRoot)
appConfig.Get<Configuration>()
Inject the configuration on your IServiceCollection
let configureServices (services: IServiceCollection) =
let config =
match Configuration.init with
| Ok config -> config
| Error e -> failwithf "Error reading configuration: %A" e
services.AddSingleton<Configuration>(config) |> ignore
services.AddGiraffe() |> ignore
Consume the configuration
let handler : HttpHandler =
fun (next: HttpFunc) (ctx: HttpContext) ->
let config = ctx.GetService<Configuration>()
let msg = sprintf "config: %s" config.SuperSecret
text msg next ctx