Reputation: 9108
As I'm polishing my little pet project, I'm trying to store all the constant strings in my app.config file (Keys, XpathExpressions etc). When I run the compiled exe this works great. In the Interactive Shell this isn't the case.
I tried to copy the .config file from my bin/Release directory to the obj/Debug & obj/Release dirs, but the Call to ConfigurationManager.AppSettings.Item("key")
always returns null.
Any suggestions how to fix this?
With best regards
Upvotes: 18
Views: 5766
Reputation: 526
Setting APP_CONFIG_FILE does the job "if it's early enough". It doesn't appear to be able to do it early enough in the .fsx file. So it needs a reset, as per this post: Change default app.config at runtime
In F#, it looks like this:
open System
open System.Configuration
open System.IO
open System.Reflection
let appConfigPath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "App.config")
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", appConfigPath)
typeof<ConfigurationManager>
.GetField("s_initState", BindingFlags.NonPublic ||| BindingFlags.Static).SetValue(null, 0)
typeof<ConfigurationManager>
.GetField("s_configSystem", BindingFlags.NonPublic ||| BindingFlags.Static).SetValue(null, null)
(typeof<ConfigurationManager>.Assembly.GetTypes()
|> Array.find (fun x -> x.FullName = "System.Configuration.ClientConfigPaths"))
.GetField("s_current", BindingFlags.NonPublic ||| BindingFlags.Static).SetValue(null, null);;
Upvotes: -1
Reputation: 17969
I think I provide below the best of both worlds from the two most voted answers above (especially for people writing fsx scripts):
Given this app.config file:
<configuration>
<appSettings>
<add key="foo" value="bar"/>
</appSettings>
</configuration>
Read foo
this way:
let appConfigPath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "app.config")
let fileMap = ExeConfigurationFileMap()
fileMap.ExeConfigFilename <- appConfigPath
let config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None)
let foo = config.AppSettings.Settings.["foo"].Value
Console.WriteLine(foo)
Upvotes: 0
Reputation: 10006
While FSI dynamically generates code for your input, using the fsi.exe.config will work just fine.
I created this file:
<configuration>
<appSettings>
<add key="test" value="bar"/>
</appSettings>
</configuration>
And saved it as "fsi.exe.config" (program files\fsharp-version\bin).
Then started FSI:
> #r "System.configuration";;
--> Referenced 'C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.configuration.dll'
> System.Configuration.ConfigurationManager.AppSettings.["test"];;
val it : string = "bar"
It also worked from Visual Studio. (But note that you'll need to reset the session to pickup changes.)
Upvotes: 12
Reputation: 1503
F# Interactive can work with executables that rely on app.config
files.
The way to do this is to have an .fs
file in your project that loads your .config
conditional on the COMPILED
define so:
let GetMyConfig() =
let config =
#if COMPILED
ConfigurationManager.GetSection("MyConfig") :?> MyConfig
#else
let path = __SOURCE_DIRECTORY__ + "/app.config"
let fileMap = ConfigurationFileMap(path)
let config = ConfigurationManager.OpenMappedMachineConfiguration(fileMap)
config.GetSection("MyConfig") :?> MyConfig
#endif
then in your script file reference the executable and #load
the .fs
file so:
#I "../Build/Path
#r "ConfiguredApp.exe"
#load "MyConfig.fs"
On executing these three lines you will see a message similar to the following in the FSI window:
[Loading C:\Svn\trunk\Source\ConfiguredApp\MyConfig.fs]
Binding session to 'C:\Svn\Qar\trunk\Build\Path\ConfiguredApp.exe'...
Notice that you're actually referencing the app.config
when in FSI (rather than the generated .exe.config
.)
Best of luck...
Upvotes: 13
Reputation: 8990
Maybe you could point FSI to the app.config file manually by using the OpenMappedExeConfiguration method on ConfigurationManager.
Also you could try loading your assembly in a separate AppDomain - you can give any file as config file to an AppDomain you create yourself using the AppDomainSetup class.
Th fact remains that FSI isn't well suited to this kind of scenario...
Upvotes: 0
Reputation: 29399
The problem is that FSI is a different exe running behind the scenes and does some crazy tricks with on-the-fly compilation and generation of binaries. Check to see which assembly FSI thinks is running. You might be surprised what you find :)
It will throw an error:
System.NotSupportedException: The invoked member is not supported in a dynamic assembly. at System.Reflection.Emit.AssemblyBuilder.get_Location()
You need to look into how to get app.config settings into dynamic assemblies. This could be a pain, and might not be worth it. If it works as a compiled binary, I'd test those things that rely on config settings outside of FSI.
Good luck.
Upvotes: 1