【问题标题】:Load dotnet project files in F# Interactive在 F# Interactive 中加载 dotnet 项目文件
【发布时间】:2018-08-09 16:25:37
【问题描述】:

我有一个 F# dotnet 项目,在我的 .fsproj(例如 <PackageReference Include="FSharp.Data" Version="3.0.0-beta4" />)中有一些依赖项。

现在我想测试我编写的模块中的一些功能,所以我启动fsharpi 并尝试使用#load "Program.fs";; 加载我的模块。

但是,我得到error FS0039: The type 'CsvProvider' is not defined

如何加载具有正确依赖项的模块?

我看到了一些解决方法,包括从一些模糊的系统相关路径(例如#load a package in F# interactive (FSharpChart.fsx))手动加载所有必需的dlls,但我想必须有更好的方法来做到这一点。

【问题讨论】:

    标签: .net f#


    【解决方案1】:

    有几个选项可以做到这一点,但没有一个是完美的。

    离子

    如果您使用的是 Ionide,则可以在 F# 视图中右键单击项目并选择“为 FSI 生成引用”。这将创建一个 F# 脚本,为您加载所有依赖项和项目文件。在下面写下你的测试代码。

    这种方法的缺点:

    • 每次项目文件或依赖项发生更改时,您都需要运行该过程。
    • 不适用于您无法访问 VS Code 的情况,尽管您可以将这些脚本签入源代码控制。

    如果您使用的是 Paket,那么您可以为您的项目生成“加载脚本”:

    dotnet paket generate-load-scripts
    

    这将在./.paket/load 中生成 F# 脚本。每个依赖项(及其传递依赖项)和每个 Paket“组”都会有一个加载脚本。

    如果您添加此行,这可以自动与您的paket.dependencies 同步:

    generate_load_scripts: true
    

    见:https://fsprojects.github.io/Paket/paket-generate-load-scripts.html

    这种方法的缺点:

    • 它可能会加载比实际需要更多的依赖项,这会降低性能
    • 您必须手动#load您的项目文件

    Dotnet + Nuget

    .NET 5 的新增功能,您可以直接在 fsx 文件中编写 Nuget 字符串。这是快速脚本的一个很棒的功能!

    #r "nuget: FParsec"
    
    // Package with a specific version
    // #r "nuget: FParsec,1.1.1"
    
    open FParsec
    
    #load "./Types.fs"
    
    // etc...
    

    这种方法的缺点:

    • 如果您在项目代码中使用 Paket,它不会将包版本与 Paket 同步
    • 您必须手动 #load 您的项目文件

    一天?

    在理想的世界中,我们可以这样做:

    #r "fsproj: ./my-proj.fsproj"
    
    open FParsec
    
    // etc...
    

    这里有一个问题:https://github.com/dotnet/fsharp/issues/8764

    【讨论】:

      【解决方案2】:

      为了访问导入库中的代码,您需要告诉 FSI 加载它们。

      在最低级别,这可以通过#r 指令来完成:

      #r "./packages/FSharp.Data/lib/whatever/FSharp.Data.dll"
      

      这通常足以满足一次性目的。

      但是,如果有很多引用,您的 IDE 通常可以为您自动执行此操作。例如,在 Visual Studio 中,您可以右键单击项目并选择“将引用发送到 F# Interactive”或“生成带有引用的脚本文件”(不确定确切的措辞)。如果您安装了 Ionide 扩展,则在 VSCode 中可以使用相同的选项。

      此外,还有一个用于直接支持 FSI 中的包的积极提案+原型 - 请参阅 https://github.com/fsharp/fslang-suggestions/issues/542。但是,这还没有合并,似乎有点停滞不前。

      【讨论】:

      • 谢谢。 Ionide 功能“生成带有引用的脚本文件”是朝着正确方向迈出的一步。但是,当我尝试加载它时,我收到另一个错误error FS0193: The module/namespace 'System' from compilation unit 'System.Runtime' did not contain the namespace, module or type 'ReadOnlySpan1'。可能我的fsharpi版本和我在项目中配置的目标框架不兼容。如果我将其从 netcoreapp2.1 更改为 net472,我会在尝试构建项目时收到错误 error MSB3644: The reference assembliesfor framework ".NETFramework,Version=v4.7.2" were not found.
      • 另外:是否有命令行工具来生成项目引用,还是仅在某些 IDE 中可用?
      【解决方案3】:

      按照 Fyodor 回答中的 cmets 的要求,这是我过去用来生成在 F# Interactive 中加载 .fsproj 所需的 #r#load 指令的脚本:

      module References
      
      #r "System.Xml"
      #r "System.Xml.Linq"
      
      open System
      open System.IO
      open System.Linq
      open System.Xml.Linq
      
      let inline isNotNull o = o |> isNull |> not
      
      let private getProject path =
          Directory.EnumerateFiles(path, "*.*proj") |> Seq.head |> XDocument.Load    
      
      let generateDlls path =
          let projectFile = getProject path
          let references =
              projectFile.Descendants <| XName.Get("HintPath", "http://schemas.microsoft.com/developer/msbuild/2003")
              |> Seq.filter (fun reference -> reference.Value.ToLower().EndsWith(".dll"))
              |> Seq.filter (fun reference -> reference.Value.StartsWith("$") |> not)
              |> Seq.map (fun reference -> reference.Value)
          let projects =
              projectFile.Descendants <| XName.Get("ProjectReference", "http://schemas.microsoft.com/developer/msbuild/2003")
              |> Seq.map (fun reference -> reference.Elements(XName.Get("Name", "http://schemas.microsoft.com/developer/msbuild/2003")).SingleOrDefault())
              |> Seq.filter (fun nameElement -> nameElement |> isNotNull)
              |> Seq.map (fun nameElement -> nameElement.Value)
              |> Seq.map (fun reference -> sprintf "../%s/bin/debug/%s.dll" reference reference)
      
          references 
          |> Seq.append projects
          |> Seq.iter (fun reference -> printfn "#r @\"%s\"" reference)
      
      let generateFs path =
          let projectFile = getProject path
          projectFile.Descendants <| XName.Get("Compile", "http://schemas.microsoft.com/developer/msbuild/2003")
          |> Seq.map (fun reference -> reference.Attribute("Include" |> XName.op_Implicit))
          |> Seq.filter (fun reference -> reference |> isNotNull && reference.Value.ToLower().EndsWith("fs"))
          |> Seq.filter (fun reference -> reference.Value.Equals("AssemblyInfo.fs", StringComparison.CurrentCultureIgnoreCase) |> not)
          |> Seq.iter (fun reference -> printfn "#load @\"%s\"" reference.Value)
      
      
      // Example Usage:
      // #load @"GenerateReferences.fsx"
      // References.generateDlls __SOURCE_DIRECTORY__
      // References.generateFs __SOURCE_DIRECTORY__
      

      我不确定这是否完全完美,例如,我认为它没有正确的依赖排序。但是,如果您想增强它,它应该是一个合理的起点。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-18
        • 1970-01-01
        • 1970-01-01
        • 2014-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多