dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.88k stars 783 forks source link

Tricky to reference .NET Standard 2.0 components from F# Interactive #3309

Closed dsyme closed 7 years ago

dsyme commented 7 years ago

Referencing a .NET Standard 2.0 component from F# Interactive (fsi.exe) fails with

"netstandard.dll 2.0.0.0" is not part of the fsi.exe application or an error such as 

or

unknown(1,1): error FS3216: type 'Microsoft.EntityFrameworkCore.Design.AnnotationCodeGeneratorDependencies' not found in assembly 'Microsoft.EntityFrameworkCore.Relational, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. A possible cause may be a version incompatibility. You may need to explicitly reference the correct version of this assembly to allow all referenced components to use the correct version.

Repro steps

Provide the steps required to reproduce the problem

  1. Create a .NET Standard 2.0 component

    dotnet new classlib -o -lang F# lib2 cd lib2 add code file: module Say let hello (t:System.DateTime) = printfn "the time is %A" t

    dotnet restore dotnet build

  2. Reference this from .NET Framework fsi.exe

Release\net40\bin\fsi.exe -r ..\misc\lib2\tplib1.dll
> Say.hello System.DateTime.Now;;

Expected behavior

Works ok and prints the current time

Actual behavior

stdin(1,1): error FS0074: The type referenced through 'System.DateTime' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard'.

Known workarounds

  1. Manually acquire the package NETStandard.Library.NETFramework

  2. Add an explicit reference to packages\NETStandard.Library.NETFramework.2.0.0-preview2-25405-01\build\net461\lib\netstandard.dll e.g.

#r @"C:\GitHub\dsyme\visualfsharp\packages\NETStandard.Library.NETFramework.2.0.0-preview2-25405-01\build\net461\lib\netstandard.dll"

Related information

master, Windows, .NET Framework 4.7 or 4.6.1

terrajobst commented 7 years ago

The best way to get the correct set is to configure the fsi application to include all .NET Standard 2.0 components necessary for .NET Framework 4.6.1 and up. This can be done by setting this property:

<Project>
  <!-- ... -->
  <PropertyGroup>
    <DependsOnNETStandard>true</DependsOnNETStandard>
  </PropertyGroup>
  <!-- ... -->
</Project>
dsyme commented 7 years ago

@cartermp @KevinRansom Given @terrajobst's advice I'd like guidance on this at some point (no huge rush). It seems that including the facade DLLs as part of the compiler seems to be the correct advice, though I think the real concern is auto-referencing them as part of the F# Interactive scripting model.

I understand the reluctance to do this. Perhaps adding just netstandard.dll is sufficient since most other facade DLLs are part of .NET 4.6.1

Anyway, I added a description of the workaround, which is to manually acquire NETStandard.Library.NETFramework and add an explicit reference to the facade DLLs you need:

#r @"packages\NETStandard.Library.NETFramework.2.0.0-preview2-25405-01\build\net461\lib\netstandard.dll"

Not pretty but it works

terrajobst commented 7 years ago

though I think the real concern is auto-referencing them as part of the F# Interactive scripting model.

Why would you need to auto-reference them? Presumably F# interfactive is using RefEmit to run code within the current process. The CLR should be able to load the appropriate assemblies on-demand from the fsi directory, no?

I understand the reluctance to do this. Perhaps adding just netstandard.dll is sufficient since most other facade DLLs are part of .NET 4.6.1

True, but customers loading pre-.NET Standard 2.0 components will get pretty bad/unactionable error messages (assembly load errors, type load errors, missing member errors).

dsyme commented 7 years ago

Why would you need to auto-reference them?

In the F# compilation stages being run in F# Interactive prior to RefEmit we need to follow the type forwarders to emit correct code (to understand that [netstandard]System.Int32 --> [mscorlib]System.Int32 etc.), to determine type equality etc.

True, but customers loading pre-.NET Standard 2.0 components will get pretty bad/unactionable error messages (assembly load errors, type load errors, missing member errors).

I agree this is technically possible, which is why I included the whole lot in my currently doomed PR. My estimate is that this doesn't actually happen much yet, for two reasons

  1. For .NET Standard 1.x components there is often a .NET Framework version in the package - or portable versions. This is what people reference if it is there. Basically most F# scripters probably aren't referencing any .NET Standard 1.x components at all.

    I think it will be much more common for nuget packages to only have .NET Standard 2.0 components - because a .NET Standard 2.0 is often completely adequate replacement for the .NET Framework version.

  2. Versions (e.g. 4.0.0.0) of some assemblies like System.IO are shipped in the implementation assemblies of .NET Framework 4.6.1. F# Interactive will happily resolve to these implementation assemblies as a last resort even if System.IO 4.1.2.0 was requested in a reference and regardless of version. While this is not "sound", I suspect the forwarders in these implementation assemblies are adequate for most .NET Standard 1.x components

terrajobst commented 7 years ago

Strike what I said earlier; I was only thinking of runtime. In order to compile the F# code to IL you of course need to load the facades. This is proof I'm not nearly as smart as many people believe (and most don't even think that highly of me to begin with 😏 )

dsyme commented 7 years ago

@terrajobst @cartermp @KevinRansom

I'll close this, since the decision was not to ship the 100 facade DLLs as part of F# Interactive, but rather apply the workaround (which feels dubious to me, but anyway...) and wait for .NET 4.7.1 to become widely spread

Workaround is:

  1. Manually acquire the package NETStandard.Library.NETFramework
  2. #r @"packages/NETStandard.Library.NETFramework.2.0.0/build/net461/lib/netstandard.dll"

Perhaps there is a better way then acquiring NETStandard.Library.NETFramework.

dsyme commented 6 years ago

I believe the fact that F# Interactive doesn't play well with .NET Standard 2.0 yet is starting to bite people https://twitter.com/isaac_abraham/status/961580320897355776

@cartermp @KevinRansom Anything we can do here?

lambdakris commented 6 years ago

HI, I was trying to use a script file within a new sdk project while targeting net471 which depends on Newtonsoft.Json. Is the right thing to do here to depend on the net45 Newtonsoft.Json.dll or on the netstandard2.0 Newtonsoft.Json.dll?

If I reference the netstandard2.0 version, I get messages like "The type 'Enum' is required here and is unavailable. You must add a reference to assembly 'netstandard, Version=2.0.0.0...'.

I tried looking for a NETStandard.Library.NETFramework package under C:\Users{username}.nuget\packages in case it is automatically acquired by the net sdk 2.x, but did not find it. Then I installed the NETStandard.Library.NETFramework package from nuget into the project, however, this did not result in the NETStandard.Library.NETFramework package being added under the .nuget/packages folder under my home folder or anywhere else that I could track down.

Ultimately I am typically unblocked by referencing the net45 version of assemblies, but I was just wondering if I should be able to reference netstandard2.0 assemblies or not...

smoothdeveloper commented 6 years ago

Starting to hit this on libraries that I can't use in FSI anymore.

Until this is fixed, could we have guidelines setup for library authors so they keep shipping binaries for full .NET framework along side netstandard ones? otherwise this is kind of splitting the ecosystem between libraries that work in FSI and those which don't.

aaron-comyn commented 6 years ago

We are seeing an issue with using Roslyn via FSI, despite using the NETStandard.Framework.Library workaround: code that compiles and runs in a .Net Core console application errors in FSI due to missing types ("type 'Microsoft.CodeAnalysis.CSharp.CSharpCompilation' not found in assembly 'Microsoft.CodeAnalysis.CSharp, Version=2.8.0.0, ...").

I have made a gist that shows how to provoke the issue: https://gist.github.com/aaron-comyn/e178636def80d898f61ce4eeead21b3b

The issue is present with the last years worth of Roslyn releases, using the same references as an operational .Net Core application. We're using FSI to introduce new datatypes into running production systems, so this issue is a bit of a 'showstopper' wrt .Net Core (System.CodeDom does not support the platform). FSI not running on .NET Standard/Core is a pain point :)

[Side note: the NETStandard.Framework.NETLibrary package is marked as deprecated... kinda confusing for FSI users who require this workaround...]


We can add EPPlus and Microsoft.Exhange.WebServices.NETStandard (unofficial .net core port of EWS), to the list. Both work fine compiled and error in FSI ( EPPlus throws a security error).

haraldsteinlechner commented 5 years ago

what is the status of this issue? is @dsyme 's workaround still the way to go? If so, by using the explicit reference i get problems alà DateTime is not compatible with DateTime. Somehow i get wrong system symbols resolved randomly. I can post a repro if relevant.

cartermp commented 5 years ago

The workaround is not appropriate, no. When #5850 is completed, then FSI will be able to understand packages and work with .NET Standard in a cross-platform way. Additionally, when VS 2019 is eventually shipped, it will have .NET Framework 4.7.1. (or 4.7.2, don't quite recall), which can understand .NET Standard "natively". The core issue is that FSI within VS has to target .NET Framework 4.6 today, and 4.6 has no notion of .NET Standard. Referencing 100 facade .dlls manually may work for you, but I can't say I'd recommend it.

OnurGumus commented 5 years ago

@dsyme even with. Net 4.7.2 similar issue hapenning with canopy

smoothdeveloper commented 5 years ago

@OnurGumus, until this is resolved, maybe canopy project can ship .net framework assemblies as well?

I believe you can still use older versions of the nuget for .net framework if that helps.

KevinRansom commented 5 years ago

@OnurGumus can you provide a repro, that fails on .NET 4.7.2, I believe it should work there fine.

OnurGumus commented 5 years ago

@KevinRansom See the attached zip file which canopy.zip only contains source but no binaries then

just call

dotnet tool install --tool-path .paket Paket
.paket\paket install
dotnet fsi test.fsx
OnurGumus commented 5 years ago

Though let me elaborate, I am targeting .net core 3.0 SDK preview 5. It's just my machine has .net 4.7.2 sdk installed too.

cartermp commented 5 years ago

It's worth noting that this is about reference .NET Standard components from the FSI that is bundled within Visual Studio, not dotnet fsi. I think it's worth filing a separate bug for dotnet fsi though, since that should work.

KevinRansom commented 5 years ago

I haven't run the repro, but I can imagine what is going on.