在F#交互中加载dotnet项目文件

6
我有一个F# dotnet项目,在我的.fsproj中有一些依赖项(例如<PackageReference Include="FSharp.Data" Version="3.0.0-beta4" />)。
现在我想测试我编写的模块中的一些函数,所以我启动fsharpi并尝试使用#load "Program.fs";;加载我的模块。
然而,接着我收到了error FS0039: The type 'CsvProvider' is not defined错误信息。
如何使用正确的依赖项加载我的模块?
我已经看到一些解决方法,其中包括手动从某些晦涩的系统相关路径加载所有必需的dll(例如#load a package in F# interactive (FSharpChart.fsx)),但我猜肯定有更好的方法来解决这个问题。
3个回答

5

有几种选项可以实现这个目标,但没有一种是完美的。

Ionide

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

这种方法的缺点:

  • 每当项目文件或依赖项更改时,都需要运行该过程。
  • 对于没有访问VS Code的情况不适用,但您可以将这些脚本检入源代码控制。

Paket

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

dotnet paket generate-load-scripts

这将在“./.paket/load”目录下生成F#脚本。每个依赖项(及其传递依赖项)和每个Paket“组”都将有一个加载脚本。
如果您添加此行,则可以自动与您的“paket.dependencies”同步:paket auto-restore true
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,则无法与其同步包版本
  • 您必须手动#load您的项目文件

有一天?

在理想的情况下,我们将能够像这样做:

#r "fsproj: ./my-proj.fsproj"

open FParsec

// etc...

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


4
为了能够访问导入库中的代码,您需要告诉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: 来自编译单元'System.Runtime'的模块/命名空间'System'不包含命名空间、模块或类型'ReadOnlySpan1'。也许我的fsharpi版本与我在项目中配置的目标框架不兼容。如果我将其从netcoreapp2.1更改为net472,则在尝试构建项目时会出现错误error MSB3644: 未找到针对框架“.NETFramework,Version=v4.7.2”的参考程序集。 - Peter Zeller
还有:是否有命令行工具可以生成项目引用,或者这只在某些IDE中可用? - Peter Zeller
3
如果您切换到Paket,可以参考https://fsprojects.github.io/Paket/paket-generate-load-scripts.html - CaringDev

0

根据 Fyodor 的回答下的评论要求,这里是我过去使用过的脚本,用于生成 #r#load 指令,以加载 F# Interactive 中的 .fsproj

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__

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接