Git Product home page Git Product logo

Comments (3)

smoothdeveloper avatar smoothdeveloper commented on August 15, 2024

@sgryt thanks for the report.

From what I understand, it is more likely to be a dotnet/sdk issue or (maybe) dotnet/fsharp one, there is nothing in the FSharp.Data.SqlClient nuget package that is related to deciding which FSharp.Core your console application should use, and the binding redirect handling is specific to msbuild, AFAIU.

I have had my own issues over the years of using dotnet framework, related to assembly bindings, and generally resort to using paket to handle the the consistency of those, but your mileage may vary.

I'll keep the ticket open to track the progress on resolving it, but encourage you to gather a minimum reproduce project, and opening an issue on dotnet/sdk.

Hope you'll be able to understand why this is occurring, and confirmation it reproduces (please provide a self contained zipped sample where it is minimum steps from command line to build, to make sure the sdk team can look into it).

from fsharp.data.sqlclient.

ronnieholm avatar ronnieholm commented on August 15, 2024

@smoothdeveloper: I'm not deeply familiar with type provider internals, but I
believe to have identified the underlying cause of the exception. Please correct
me if my reasoning is off.

The exception appears to be caused by a general limitation across type providers
when the F# compiler is referencing a different version of FSharp.Core than the
application being compiled.

Code like

type Datawarehouse = SqlProgrammabilityProvider<"name=DataWarehouse">
let checkReady = new Datawarehouse.read_json.check_if_ready_for_load(connectionString)
async {
    // Exception raised here. isReady is of type option<bool> which is important later.
    let! isReady = checkReady.AsyncExecute()
    ...
}

results in

System.IO.FileLoadException: 'Could not load file or assembly 'FSharp.Core, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)'

with a stack trace of

[Managed to Native Transition]	
mscorlib.dll!System.RuntimeTypeHandle.GetTypeByName(string name, bool throwOnError, bool ignoreCase, bool reflectionOnly, ref System.Threading.StackCrawlMark stackMark, System.IntPtr pPrivHostBinder, bool loadTypeFromPartialName)	Unknown
mscorlib.dll!System.Type.GetType(string typeName, bool throwOnError)	Unknown
FSharp.Data.SqlClient.dll!FSharp.Data.SqlClient.Internals.ISqlCommand Implementation.ISqlCommand Implementation(FSharp.Data.SqlClient.Internals.DesignTimeConfig cfg, Microsoft.FSharp.Core.FSharpChoice<string, System.Data.SqlClient.SqlConnection, System.Data.SqlClient.SqlTransaction> connection, int commandTimeout)	Unknown

The offending type provider code is at
https://github.com/fsprojects/FSharp.Data.SqlClient/blob/master/src/SqlClient/ISqlCommand.fs#L111:

let itemType = Type.GetType( itemTypeName, throwOnError = true)

Here itemTypeName is a "type string", baked into the application by the
SqlClient.DesignTime.

De-sugaring the async computation expression above and reverse compiling the
design-time generated code to C#, DesignTimeConfig (from the stack trace) is
instantiated as

ISqlCommand\u0020Implementation checkReady = (ISqlCommand\u0020Implementation)new ISqlCommand\u0020Implementation(new DesignTimeConfig("SELECT read_json.check_if_ready_for_load()", false, Array.Empty<SqlParameter>(), ResultType.Records, ResultRank.ScalarValue, Mapper.GetMapperWithNullsToOptions(Dsl.Consumption.checkReady@170.@_instance, Dsl.Consumption.checkReady@170-1.@_instance), "Microsoft.FSharp.Core.FSharpOption`1[[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], FSharp.Core, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", new Tuple<string, string>[]
			{
				new Tuple<string, string>("", "System.Boolean")
			}), FSharpChoice<string, SqlConnection, SqlTransaction>.NewChoice1Of3(item), commandTimeout);

Notice how part of the instantiation consists of FSharp.Core, Version=8.0.0.0:

"Microsoft.FSharp.Core.FSharpOption`1[[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], FSharp.Core, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

which is the return type from calling the type provider and the underlying
source of the exception.

Digging through SqlClient.DesignTime, I came upon
https://github.com/fsprojects/FSharp.Data.SqlClient/blob/master/src/SqlClient.DesignTime/DesignTime.fs#L31:

| Some x -> Expr.Value( x.ErasedTo.AssemblyQualifiedName)

where I believe x.ErasedTo is the System.Type returned by the type provider,
i.e., option<bool> in this case.

The underlying issue can be illustrated by creating a .NET console application
and in its project file pin FSharp.Core to a version different from the latest
version installed on the machine (.NET 4.8 and .NET 8 in this case):

<PackageReference Update="FSharp.Core" version="7.0.0" />

Then include the following code:

open Microsoft.FSharp.Quotations

[<EntryPoint>]
let main _ =
    let t: System.Type = typeof<option<bool>>
    let e: Expr = Expr.Value(t.AssemblyQualifiedName)
    printfn "%A" e
    // Value ("Microsoft.FSharp.Core.FSharpOption`1[
    //   [System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], 
    //     FSharp.Core, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
    0

Not pinning FSharp.Core makes e become Version=8.0.0.0.

As during compilation FSharp.Data.SqlClient.DesignTime.dll is loaded into the
F# compiler process, code in the design-time executes in the context of the
compiler, i.e., not subject to any assembly redirect in the program being
compiled. The compiler itself doesn't have an assembly redirect downgrading its
version of FSharp.Core to 7.0.0.0.

When the equivalent of

let t: System.Type = typeof<option<bool>>
let e: Expr = Expr.Value(t.AssemblyQualifiedName)

evaluates inside the compiler, the result is a "type string" containing
FSharp.Core, Version=8.0.0.0.

Now, the compiler is aware of the FSharp.Core version it's compiling the
application against. MSBuild resolves and passes this information on the
command-line when invoking the compiler:

C:\Program Files\Microsoft Visual Studio\2022\Community/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/fscAnyCpu.exe -o:obj\Debug\net48\Test.exe
...
-r:C:\Users\tester\.nuget\packages\fsharp.core\7.0.0\lib\netstandard2.0\FSharp.Core.dll
...
Dsl.fs
Program.fs

But this information may be lost/hard to get at inside the design-time of a type
provider -- at least with current type provider infrastructure.

So either (1) upgrade the application to the latest version of F# installed, (2)
include an assembly redirect downgrading FSharp.Core, or (3) add a
global.json pinning the .NET version.

from fsharp.data.sqlclient.

smoothdeveloper avatar smoothdeveloper commented on August 15, 2024

I believe to have identified the underlying cause of the exception. Please correct me if my reasoning is off.

I'm unlikely to "correct", but share my understanding (which can be misguided, as this is either a fsharp or TP SDK issue, not an dotnet/sdk one), and your analysis seems thorough (based on looking up IL types emitted by the compiler, and investigating the quotations in the type provider implementation).

I believed the binding redirects were not needed anymore but in .NET framework, but it sounds I was too hopeful.

The analysis you make about the TP in the compiler is correct, I'm not sure if the newer versions of the Type Provider SDK (we are on one of the early that supported .net core, sdk v2 era) help solving the issue, nor if the type provider implementation should define things for the TP SDK to do the relinking, I only know any time I tried to upgrade the TP SDK, it was a bit though, or I couldn't make it work (but also due to sdk v2 in the master branch and not enough know how).

Let's keep the issue open (others will find useful information, and learn about binding redirect when they have same error), and when I'm able to update Type Provider SDK while keeping everything green on CI, we should give it another look, in case there is some form of detection between runtime references versus what the compiler binds against.

Thanks @ronnieholm for giving a shot at reproducing @sgryt issue, and all the analysis.

For now, I effectively can't solve those use cases, and only encourage people to closely care for their binding redirects, msbuild or paket can be used to deal with it in ways which aren't 100% manual.

from fsharp.data.sqlclient.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.