繁体   English   中英

在 F# Interactive 中加载 dotnet 项目文件

[英]Load dotnet project files in F# Interactive

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

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

但是,然后我收到error FS0039: The type 'CsvProvider' is not defined

如何使用正确的依赖项加载我的模块?

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

为了访问导入库中的代码,您需要告诉 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,那么您可以在 F# 视图中右键单击一个项目并选择“为 FSI 生成引用”。 这将创建一个 F# 脚本,为您加载所有依赖项和项目文件。 在下面写你的测试代码。

这种方法的缺点:

  • 每次项目文件或依赖项更改时,您都需要运行该过程。
  • 不适用于您无法访问 VS Code 的情况

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

dotnet paket generate-load-scripts

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

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

generate_load_scripts: true

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

这种方法的缺点:

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

点网 + 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

正如在Fyodor的答案意见要求,这里就是我已经在过去用来生成一个脚本#r#load来加载所需的指令.fsproj在F#互动:

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__

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM