Skip to content

Commit 47d7855

Browse files
github-actions[bot]Copilot
authored andcommitted
fix: resolve FSharp.Core version mismatch in item type resolution (Closes #433)
When the F# compiler process runs at a different FSharp.Core version than the compiled project (e.g. compiler has 8.0.0.0, project is pinned to 7.0.0.0), the assembly-qualified type name baked into the quoted expression by the design time DLL contains the compiler's FSharp.Core version. At runtime, Type.GetType then fails with FileLoadException because the requested assembly version is not loaded. Fix: try the exact assembly-qualified name first; if that returns null, strip Version/Culture/PublicKeyToken from all assembly references in the name and retry, allowing the runtime's own assembly-binding logic to locate the correct version of FSharp.Core (or any other assembly affected by the same pattern). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b488403 commit 47d7855

File tree

1 file changed

+19
-1
lines changed

1 file changed

+19
-1
lines changed

src/SqlClient/ISqlCommand.fs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ open System
2121
open System.Data
2222
open System.Data.SqlClient
2323
open System.Reflection
24+
open System.Text.RegularExpressions
2425
open FSharp.Data.SqlClient
2526
open FSharp.Data.SqlClient.Internals
2627
open System.Linq
@@ -33,6 +34,23 @@ module Seq =
3334
| [| x |] -> Some x
3435
| _ -> invalidArg "source" "The input sequence contains more than one element."
3536

37+
module internal TypeResolution =
38+
39+
// Resolves a type by its assembly-qualified name, with a version-tolerant fallback.
40+
// The design-time DLL runs inside the F# compiler process and bakes the compiler's FSharp.Core
41+
// version into item type names. If the user's project references a different FSharp.Core version,
42+
// Type.GetType with the exact name fails with FileLoadException. Stripping version/culture/token
43+
// from the assembly reference lets the runtime's binding logic find the correct assembly.
44+
// See: https://github.com/fsprojects/FSharp.Data.SqlClient/issues/433
45+
let resolveType (typeName: string) =
46+
match Type.GetType(typeName, throwOnError = false) with
47+
| null ->
48+
let stripped = Regex.Replace(typeName, @",\s*Version=[^,\[\]]+", "")
49+
let stripped = Regex.Replace(stripped, @",\s*Culture=[^,\[\]]+", "")
50+
let stripped = Regex.Replace(stripped, @",\s*PublicKeyToken=[^,\[\]]+", "")
51+
Type.GetType(stripped, throwOnError = true)
52+
| t -> t
53+
3654
[<CompilerMessageAttribute("This API supports the FSharp.Data.SqlClient infrastructure and is not intended to be used directly from your code.", 101, IsHidden = true)>]
3755
type RowMapping = obj[] -> obj
3856

@@ -108,7 +126,7 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio
108126
notImplemented
109127
| rowMapping, itemTypeName ->
110128
assert ((not(isNull rowMapping)) && (not (isNull itemTypeName)))
111-
let itemType = Type.GetType( itemTypeName, throwOnError = true)
129+
let itemType = TypeResolution.resolveType itemTypeName
112130

113131
let executeHandle =
114132
typeof<``ISqlCommand Implementation``>

0 commit comments

Comments
 (0)