簡體   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