Using the FSharp.Configuration type provider - f#

I'd like to the use app.config file of my F# to store versioning information. I discovered the FSharp.Configuration type provider which seemed like it'd be simple enough. However, I'm running in to an error I can't diagnose.
Below is a screen shot of a version.config file (identical to the one in the link above) and a scratch pad.
As you can see, calling Settings auto populates a drop-down of everything in the <appSettings> chunk of the config but when I try to run something,
I get an error saying that the thing I'm looking for can't be found in the <appSettings> section of the config file.
What's causing this error, especially considering that it clearly is finding it in the config file, given it's auto-populating? What can I do to prevent this from happening again?

You have bumped into this issue.
When you run the Configuration provider in FSI it will look not for the app's config file but FSI's config file. One way to get around this is by specifying the exe's config file explicitly. Here's an example:
open FSharp.Configuration
open System
type Settings = AppSettings<"app.config">
[<EntryPoint>]
let main argv =
let path = System.IO.Path.Combine [|__SOURCE_DIRECTORY__ ;"bin";"release";"ConfigApplication.exe" |]
Settings.SelectExecutableFile path
Settings.TestBool <- false // change a setting
printfn "%A" Settings.Test2 // read another setting
Console.ReadLine() |> ignore
0 // return an integer exit code
This will take the App.config file in the source directory, but use the ConfigApplication.exe.config file in the binaries directory.
If you just need to set the DB's connection string, it's actually easier, if the SQL type provider has a config setting parameter, just specify the config file there (and set it to Always copy in VS), if you add that to .gitignore you can have many different app.config files with different connection strings.
You could also use the YAML provider, it has two advantages, it's not XML and it's not an erasing type provider.

Related

Reading the content of directory declared with `actions.declare_directory`

Imagine I have a java_binary target triggered by a custom rule that generates source code and places the generated sources under a directory, let's call it "root".
So after the code generation we will have something like this:
// bazel-bin/...../src/com/example/root
root:
-> Foo.java
-> Bar.java
-> utils
-> Baz.java
Now, I have another target, a java_library, that depends on the previously generated sources, so it depends on the custom rule.
My custom rule definition currently looks something like this:
def _code_generator(ctx):
outputDir = ctx.actions.declare_directory("root")
files = [
ctx.actions.declare_file("root/Foo.java"),
ctx.actions.declare_file("root/Bar.java"),
ctx.actions.declare_file("root/utils/Baz.java"),
// and many,
// many other files
]
outputs = []
outputs.append(outputDir)
outputs.extend(files)
ctx.actions.run(
executable = // executable pointing to the java_binary
outputs = outputs
// ....
)
This works. But as you can see, every anticipated file that is to be generated, is hard-coded in the rule definition. This makes it very fragile, should the code generation produce a different set of files in the future (which it will).
(Without specifying each of the files, as shown above, Bazel will fail the build saying that the files have no generating action)
So I was wondering, is there a way to read the content of the root directory and automatically, somehow, declare each of the files as an output?
What I tried:
The documentation of declare_directory says:
The contents of the directory are not directly accessible from Starlark, but can be expanded in an action command with Args.add_all().
And add_all says:
[...] Each directory File item is replaced by all Files recursively contained in that directory.
This sounds like there could be a way to get access to the individual files in the directory, but I am not sure how.
I tried:
outputDir = ctx.actions.declare_directory("root")
//...
args = ctx.actions.args()
args.add_all(outputDir)
with the intention to access the individual files later from args, but the build fails with: "Error in add_all: expected value of type sequence or depset for values, got File".
Any other ideas on how to implement the rule, so that I don't have to hard-code each and every file that will be generated?

Access Denied when creating file in Visual F#

The following code runs without a hitch:
On the other hand, I get an access-denied error with this:
The destination is in my personal folder and I have full control. The directory is not read-only. Anyway, in either of those cases, the first code sample should not run either! I appreciate the help ...
In the second sample, you have two problems:
There are back slashes instead of forward slashes, so some of them may get interpreted as escape sequences.
You completely ignore the first parameter of write and specify what I assume is a folder as destination. You can't open a file stream on a folder, no wonder you get access denied.
This should work:
let write filename (ms:MemoryStream) =
let path = System.IO.Path.Combine( "C:/Users/<whatever>/signal_processor", filename )
use fs = new FileStream( path, FileMode.Create )
ms.WriteTo(fs)

Fsx execution path

I have a c# .net library I am looking to use within FSI/FSX. As part of the initialization of the .net lib, by default it expects and references a custom config file (MyAppConfig.xml) which loads various things before it can be used. When using it in c# it gets copied to the bin folder and the app by default expects it to be there and references it there unless there is a specific entry in the app.config to tell it otherwise. (I should add that it does it all by convention rather than injecting a path + filename, as per NLog, say)
I have an f# source file in a console app which will execute this initialization find, but I can't quite work out how to achieve this with FSI/FSX.
So my program.fs looks simply like
open System
open myApp
module Program =
[<EntryPoint>]
let Main(args) =
myApp.Initialization.Load() // references MyAppConfig.xml
Console.WriteLine("do my stuff!")
Console.ReadLine() |> ignore
0
If I try and do the same in FSI or using FSX, I have
#r #"E:\...path to MyApp...\MyApp.dll"
#I #"E:\...path to MyAppConfig.xml ..."
Environment.CurrentDirectory <- #"E:\...path to MyAppConfig.xml ..."
myApp.Initialization.Load() |> ignore // fails ... can't find MyAppConfig.xml
//do my stuff
I suspect that I've not got the paths quite right.
I'd be grateful of a steer
EDIT:
So I've managed to attach a debugger to the c# lib and see where it is looking for the config file - turns out it is "c:\Program Files\Microsoft F#\v4.0\" ( System.AppDomain.CurrentDomain.BaseDirectory) which again shows I've not quite understood how to tell FSI/FSX to use a particular path. If I copy the config file (MyAppConfig.xml) to that location it works fine.
Many thx
S
I'm not sure of the implications, but one possiblity might be temporarily changing the app base:
let origAppBase = AppDomain.CurrentDomain.BaseDirectory
AppDomain.CurrentDomain.SetData("APPBASE", "path_to_MyAppConfig.xml")
myApp.Initialization.Load() |> ignore
AppDomain.CurrentDomain.SetData("APPBASE", origAppBase) //restore original app base

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>

ILASM does not set FileVersion

I have an .il file which I can compile without any problems. I can strong name it and so without any issues. But I am not able to set the file version via the attribute as I would expect it. How can I set the FileVersion for an assembly when using ilasm?
If I do a round trip I get always a .res file which does contain only binary data which is not readable. What is inside this res file and can I edit it?
The code does not work
.assembly myAssembly
{
.custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = { string('1.2.3.4') }
The issue can be solved by using the .res file. It is not sufficient to do a round trip with ildasm and ilasm. The IL file does not reference the .res file. I had to add it to the ilasm call manually. The data in the res file seemed to contain the infos which are written into the PE header which is ok for me.
The final command line needed was
ilasm test.il /dll /res:test.res
I still do not know what exactly is inside the res file but I can exhange it with the meta data information of any other assemlby that I create manually and then decompile it to replace the metadata of the original assembly as I need.
It seems not many people are doing such stuff.

Resources