【问题标题】:App.config and F# Interactive not workingApp.config 和 F# Interactive 不工作
【发布时间】:2010-10-13 08:04:40
【问题描述】:

在我完善我的小宠物项目时,我试图将所有常量字符串存储在我的 app.config 文件中(键、XpathExpressions 等)。当我运行编译后的 exe 时,效果很好。在交互式 Shell 中,情况并非如此。

我尝试将 .config 文件从我的 bin/Release 目录复制到 obj/Debug & obj/Release 目录,但对 ConfigurationManager.AppSettings.Item("key") 的调用始终返回 null。

有什么建议可以解决这个问题吗?

致以最诚挚的问候

【问题讨论】:

    标签: .net f#


    【解决方案1】:

    设置 APP_CONFIG_FILE “如果足够早”就可以完成这项工作。它似乎无法在 .fsx 文件中尽早完成。所以它需要重置,根据这篇文章: Change default app.config at runtime

    在 F# 中,它看起来像这样:

    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);;
    

    【讨论】:

      【解决方案2】:

      我认为我从上面两个投票最多的答案中提供了两全其美的答案(尤其是对于编写 fsx 脚本的人):

      鉴于此 app.config 文件:

      <configuration>
          <appSettings>
              <add key="foo" value="bar"/>
          </appSettings>
      </configuration>
      

      以这种方式阅读foo

      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)
      

      【讨论】:

      • “ExeConfigurationFileMap”从何而来?
      • 这很好,但它仍然无法使 ConfigurationManager.AppSettings 或 ConfigurationManager.GetSection 工作 - 如果没有重构,您尝试插入的任何现有库仍然无法工作。
      【解决方案3】:

      虽然 FSI 为您的输入动态生成代码,但使用 fsi.exe.config 就可以正常工作。

      我创建了这个文件:

      <configuration>
          <appSettings>
              <add key="test" value="bar"/>
          </appSettings>
      </configuration>
      

      并将其保存为“fsi.exe.config”(程序文件\fsharp-version\bin)。

      然后开始 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"
      

      它也适用于 Visual Studio。 (但请注意,您需要将会话重置为拾取更改。)

      【讨论】:

      • 这对我有用。我想为 WCF 客户端添加“....”。我将此添加到现有的“fsi.exe.config”中,重新启动 FSI,然后我的“let client = new ServiceClient”工作:)
      • 对于 VS 2015,F# 配置位于 C:\Program Files (x86)\Microsoft SDKs\F#\4.0\Framework\v4.0 下。
      • 这种方法的真正缺点是必须将配置文件放在该位置(对于 fsx 脚本,最好的解决方案是配置文件可以位于与脚本相同的文件夹中,如它是在stackoverflow.com/questions/13256167/…中请求的)
      • 谢谢。对于那些试图从 fsx 使用基于 EntityFramework 的库的人来说,在您的解决方案中引用该库就足够了,然后在 fsi.exe.config 中包含相关的连接字符串
      【解决方案4】:

      F# Interactive 可以使用依赖于 app.config 文件的可执行文件。

      这样做的方法是在您的项目中有一个.fs 文件,该文件加载您的.config,条件是COMPILED 定义如下:

      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
      

      然后在您的脚本文件中引用可执行文件和#load .fs 文件,这样:

      #I "../Build/Path

      #r "ConfiguredApp.exe"

      #load "MyConfig.fs"

      执行这三行时,您将在 FSI 窗口中看到类似于以下内容的消息:

      [Loading C:\Svn\trunk\Source\ConfiguredApp\MyConfig.fs]

      Binding session to 'C:\Svn\Qar\trunk\Build\Path\ConfiguredApp.exe'...

      请注意,在 FSI 中您实际上是在引用 app.config(而不是生成的 .exe.config。)

      祝你好运……

      【讨论】:

      • 我知道这是 10 多年后的事了,但我认为这不适用于 .NET5(使用 dotnet fsi 运行),对吧?
      【解决方案5】:

      也许您可以使用 ConfigurationManager 上的 OpenMappedExeConfiguration method 手动将 FSI 指向 app.config 文件。

      您也可以尝试在单独的 AppDomain 中加载您的程序集 - 您可以将任何文件作为配置文件提供给您使用 AppDomainSetup 类自己创建的 AppDomain。

      事实仍然是 FSI 不太适合这种情况...

      【讨论】:

        【解决方案6】:

        问题在于,FSI 是一个在幕后运行的不同 exe,它通过动态编译和生成二进制文件做了一些疯狂的技巧。查看哪个assembly FSI thinks is running。你可能会对你的发现感到惊讶:)

        会报错:

        System.NotSupportedException: 调用的成员在 a 中不受支持 动态装配。在 System.Reflection.Emit.AssemblyBuilder.get_Location()

        您需要研究如何将 app.config 设置放入动态程序集中。这可能会很痛苦,而且可能不值得。如果它作为编译后的二进制文件工作,我会测试那些依赖于 FSI 之外的配置设置的东西。

        祝你好运。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-22
          相关资源
          最近更新 更多