Skip to content

Update to .NET 10 / EF Core 10#168

Open
techiedesu wants to merge 19 commits intoefcore:masterfrom
techiedesu:update-to-net10
Open

Update to .NET 10 / EF Core 10#168
techiedesu wants to merge 19 commits intoefcore:masterfrom
techiedesu:update-to-net10

Conversation

@techiedesu
Copy link
Copy Markdown

Summary

  • Migrate EFCore.FSharp from .NET 6 / EF Core 6 to .NET 10 / EF Core 10
  • Update all dependencies (FSharp.Core, FSharp.Compiler.Service, paket, fantomas, FAKE)
  • Adapt library and test code to EF Core 10 breaking changes (removed/renamed APIs, new interface members, constructor changes)
  • Add C# interop project for TestRelationalTypeMappingSource (F# cannot handle C# covariant return types)
  • Update CI workflow, devcontainer, VS Code configs, and documentation for .NET 10
  • Replace deprecated PackageLicenseUrl with PackageLicenseExpression (MIT)
  • Relax FSharp.Core dependency constraint to >= 9.0.100

Checklist

  • I have followed the Contribution Guidelines
  • I have added tests (if appropriate)
  • I have added necessary documentation (if appropriate)

Test plan

  • All 84 existing tests pass on all platforms (ubuntu, windows, macOS)
  • Fantomas formatting check passes
  • NuGet package builds correctly with proper metadata (MIT license, net10.0 TFM)
  • End-to-end test: created sample F# project with EF Core migrations (scaffolding, applying, SQLite)
  • Devcontainer Docker image builds successfully
  • CI green on all 3 platforms

🤖 Generated with Claude Code

- global.json: SDK 6.0.100 -> 10.0.100
- All projects: net6.0/net5.0 -> net10.0
- Package version: 6.0.0-local001 -> 10.0.0-local001
- actions/checkout v2 -> v4
- actions/setup-dotnet v1.9.0 -> v4
- dotnet-version: 6.x -> 10.x
- Rename "Setup .NET Core SDK" to "Setup .NET SDK"
- Remove obsolete DOTNET_ROOT workaround comments
dotnet-tools.json:
- paket: 6.0.13 -> 10.3.1
- fantomas-tool 4.5.6 -> fantomas 6.3.15
- reportgenerator: 4.2.15 -> 5.4.1

paket.dependencies:
- FSharp.Core: 6.0.1 -> 9.0.100
- Microsoft.EntityFrameworkCore.*: ~> 6.0.0 -> ~> 10.0.0
- FSharp.Compiler.Service: 41.0.1 -> 43.9.100
- FAKE: 5.20.4 -> 6.1.4
- Octokit: 0.48 -> >= 13.0.1
- Microsoft.NET.Test.Sdk: 17.0.0 -> 17.12.0
EntityFrameworkExtensions:
- NamespaceComparer moved to Microsoft.EntityFrameworkCore.Design
- Remove GetEntityTypeErrors (removed in EF Core 7)

EFCoreFSharpServices:
- ICSharpEntityTypeGenerator/ICSharpDbContextGenerator interfaces
  removed in EF Core 7; register concrete types directly

FSharpHelper (ICSharpHelper interface):
- Fragment: MethodCallCodeFragment -> IMethodCallCodeFragment
- Add Fragment overloads: IMethodCallCodeFragment+indent,
  NestedClosureCodeFragment, PropertyAccessorCodeFragment,
  AttributeCodeFragment
- Add Identifier<T> overload with IDictionary
- Add Lambda(IEnumerable<IProperty>) overload
- Add Literal: DateOnly, TimeOnly, BigInteger, List<T>, Dictionary
- Literal(Enum) gains fullName parameter
- Namespace: add ParamArray attribute
- Add XmlComment, Arguments, GetRequiredUsings, Statement, Expression
- Fix AppendLine(FormattableString) ambiguity with type annotation

FSharpDbContextGenerator:
- ISequence.ClrType -> .Type
- Remove ScaffoldingAnnotationNames.EntityTypeErrors
- Remove GetEntityTypeErrors loop
- Convert ICSharpDbContextGenerator interface to member

FSharpEntityTypeGenerator:
- GetPropertiesAndNavigations -> GetMembers
- Convert ICSharpEntityTypeGenerator interface to member

FSharpModelGenerator:
- Use concrete generator types instead of removed interfaces
- ScaffoldedFile: named-param constructor -> positional constructor

FSharpMigrationsScaffolder:
- Save: add dryRun parameter, guard file writes
- Fix File.WriteAllText ReadOnlySpan<char> overload ambiguity

FSharpSnapshotGenerator:
- Fragment(c, builderName) -> Fragment(c, builderName, false)
  (typeQualified parameter now required)
F# cannot handle C# covariant return types used by
RelationalTypeMappingSource in EF Core 10 (new virtual methods with
sealed bridge methods). The F# compiler doesn't see the bridge methods
as implementing abstract members from TypeMappingSourceBase.

Move TestRelationalTypeMappingSource to a C# project and have the
F# test project reference it. Also update the F# stub to inherit
from the C# base class and fix Clone -> WithStoreTypeAndSize rename.
Mocks.fs:
- Add IHistoryRepository members: Create, CreateAsync,
  LockReleaseBehavior, AcquireDatabaseLock, AcquireDatabaseLockAsync

SqlServerTestHelpers.fs, RelationalTestHelpers.fs:
- UseProviderOptions now returns DbContextOptionsBuilder
  (was void in EF Core 6)

FakeDbConnection.fs:
- Remove AppendIdentityWhereCondition, AppendSelectAffectedCountCommand,
  AppendRowsAffectedWhereCondition (removed from UpdateSqlGenerator)

BuildReference.fs:
- Replace CompileToDynamicAssembly with Compile + Assembly.Load
  (CompileToDynamicAssembly removed in FSharp.Compiler.Service 43)
- Shared FSharpChecker instance with lock for thread safety
FSharpMigrationOperationGeneratorTest:
- SqlServerTypeMappingSource: pass SqlServerSingletonOptions()
  instead of Unchecked.defaultof (NRE in constructor)
- WKTReaderType -> WktReaderType
- Add namespace declaration to generated F# code
- Type annotations for Assembly/MethodInfo reflection calls

FSharpMigrationsGeneratorTest:
- SqlServerSingletonOptions() for SqlServerTypeMappingSource
- Filter RelationalAnnotationNames to string fields only
- SortedDictionary -> Dictionary for DbFunctions
- FinalizeModel: add skipValidation for annotation check tests
- Update annotation exclusion lists with ~35 new EF Core 7-10
  annotations (stored procedures, mapping strategies, JSON, etc.)
- HasComment -> HasAnnotation for entity type comment
- Remove DefiningQuery (removed in EF Core 7)
- configure -> configureConventions parameter rename

FSharpMigrationsScaffolderTest:
- SqlServerSingletonOptions() for SqlServerTypeMappingSource
- MigrationsModelDiffer dependencies: IChangeDetector ->
  IRelationalAnnotationProvider, IUpdateAdapterFactory ->
  IRowIdentityMapFactory
- MigrationsScaffolder: add 4 new required dependencies
- Save: add dryRun=false parameter

ModelCodeGeneratorTestBase:
- CreateConventionBuilder: use addServices parameter instead of
  passing stripped-down contextServices
- Remove GetEntityTypeErrors call
- FinalizeModel: remove skipValidation (use default validation)
- Changelog.New: add missing 5th parameter (references list)
- Regex.IsMatch: add type annotation to resolve overload ambiguity
- Run fantomas on all source, test, and build files (48 files formatted)
- Update SDK version requirements in README.md, GETTING_STARTED.md,
  and docsSrc/Tutorials/Getting_Started.md (.NET 5.0 -> 10.0)
- Add CHANGELOG.md entry for 10.0.0 release
- Add explicit fantomas formatting settings to .editorconfig to prevent
  mass reformatting on future fantomas upgrades
- Update .NET SDK download link to https://dotnet.microsoft.com/en-us/download
- Dockerfile: debian buster -> bookworm, update lib dependencies
- install-dotnets.sh: remove hardcoded .NET 5.0, rely on global.json
- devcontainer.json: extensions -> customizations.vscode.extensions
- Remove deprecated FSharp.fsacRuntime setting from VS Code configs
- FSharp.Core: exact 9.0.100 -> >= 9.0.100 (fixes NU1608 warning
  when consumers use a newer FSharp.Core from their SDK)
- Rename build/net5.0 -> build/net10.0 in NuGet package
- Fix hardcoded net6.0 TFM in docsTool/Program.fs (should be net10.0)
- Move CSharpInterop project to paket (was using hardcoded preview EF Core version)
- Update AssemblyInfo.fs files from 6.0.x to 10.0.0
- Replace deprecated PackageLicenseUrl with PackageLicenseExpression (MIT)
- Replace failwith "todo" stubs with proper no-op implementations in FakeDiagnosticsLogger
- Remove commented-out TryAddProviderSpecificServices dead code
- Format docsTool/templates/types.fs with fantomas
@techiedesu
Copy link
Copy Markdown
Author

@simon-reynolds @kant2002 could you review this? 👀

Reverts mass-reformatting introduced by fantomas 6.3.15 upgrade.
Formatting changes should be reviewed separately from functional
.NET 10 migration changes as requested by reviewers.
@techiedesu
Copy link
Copy Markdown
Author

Reverted all formatting changes. So sorry for formatting, my mistake 🙏

@techiedesu techiedesu requested a review from kant2002 April 6, 2026 14:46
member this.Literal(value: string) : string = this.literalString value

member this.Literal(value: TimeOnly) : string =
if value.Ticks % 10_000L = 0L then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like mistake

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed. Now follows the same logic as CSharpHelper.Literal(TimeOnly).


let nameMatches =
navigation.DeclaringEntityType.GetPropertiesAndNavigations()
navigation.DeclaringEntityType.GetMembers()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like oversight and this will include fields and methods.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. GetProperties + GetNavigations looks better


let rlNames =
(typeof<RelationalAnnotationNames>).GetFields()
|> Seq.filter (fun f -> f.FieldType = typeof<string>)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious why this change is required?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api change

RelationalAnnotationNames now has a non-string AllNames field. GetFields() picks it up and the test breaks. Filter keeps only strings 🤷‍♀️

_toTable
+ @"entityTypeBuilder.HasComment(""My Comment"") |> ignore"))
(CoreAnnotationNames.DefiningQuery, (box (Expression.Lambda(Expression.Constant(null))), ""))
+ @"entityTypeBuilder.HasAnnotation(""Relational:Comment"", ""My Comment"") |> ignore"))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like different test then original. No?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, different — old behavior doesn't work in EF Core 10. HasComment on entity is now obsolete, supposed to go inside ToTable(t => t.HasComment()). Updated generator and test to match.

@techiedesu techiedesu requested a review from kant2002 April 7, 2026 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants