fluentmigrator: Migrations not found when defined in F#

Migrations defined in F# fail to be discovered by FluentMigrator when defined in F#.

I have tested this a plethora of ways from F#, each resulting in a different error.

  1. Running using FAKE a) When running without explicitly loading FluentMigrator and FluentMigrator.Abstractions into the load context, these assemblies cannot be found.

    b) When running with these explicitly loaded, a MissingMigrationsException is thrown, even though, to the best of my knowledge, the appropriate class is discoverable.

  2. Running out of process When running using the out of process tool, the tool fails to find FSharp.Core

  3. Running as a separate console application (defined in F#) When running in the separate console application, a SIGSEV is thrown.

I don’t think this library explicitly supports F#, and I understand that, however while result 1a seems weird, 1b is really what I’m opening an issue about.

I can reflectively find my Migration’s type, and confirm it has the Migration attribute on it. Additionally it implements Migration, so it should be discoverable.

I’ve run this on the latest core SDK, as well as the latest preview SDK.

I have provided a minimal solution to display the issue.

to run from Fake, the command should be as follows:

  1. .paket/paket.exe install (use mono if not on windows)
  2. dotnet build build.proj

[EDIT]

Fixed problem 1, the migration assembly was being loaded into the wrong context, this is not a FluentMigrator issue.

I will leave this issue open in case 2 and 3 are of interest.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 50 (3 by maintainers)

Commits related to this issue

Most upvoted comments

@cartermp No worries - and I hope my comments about disliking F# are not taken personally. I was a huge fan of F# in the early days but just gave up the evangelism battle, especially when I ran into stability issues. Still hope F# teaches the CLR team to think more robustly about the CLR type system.

GenericProcessorBase.cs should probably pass the connection string into a connection string builder to provide a better error message. However, this error indicates that the issue is simply that you didn’t tell it the connection string.

In this case, please:

  1. open a bug for “dotnet-fm list migrations shouldn’t require connection string”. I think this is a separate issue. The workaround is to supply a connection string to a database.
  2. (Possibly) Open a feature request for list migrations command with more options, such as listing migrations available vs. migrations not yet applied to a target database (which would require a connection string). Please provide feedback here on whether you think this is needed.

@jzabroski you do not have to specify type everywhere:

let action = Action<string>(fun x -> printfn "%s" x |> ignore)

or you may even do like this:

Alright, I pushed the latest version and cleaned it up a bit based on your suggestions. Let me know if you think this is a publishable F#-style, or if any other improvements are needed.

@voroninp I spent my Saturday evening putting together a sample of an F# In-Process Runner using FluentMigrator. I am by no means an F# expert and last wrote F# code in January as part of a project to port F# logic to C#. In particular, I found some of the syntax F# seemed to require me to write to interface with C# logic:

  1. Weird conventions around how F# auto-getters and setters work - why do I have to define the type twice, and why does it only work well if I define the initial value via type constructor (I think this is because you have to allocate some value, but it wasn’t clear from the F# documentation)
  2. Wrapping Action<T> requires extra type parameter noise compared to C# (surprisingly)

However, I can guarantee you I got this working. Please see: https://github.com/jzabroski/FSharpFluentMigratorCli/tree/master

Up next to is to debug the issues with running this from Fake, but for that I need more information from @tylerhartwig please.

BTW, @cartermp - if interested, feel free to code review and correct me if my above experience is based on amateurish insight 😃

Pushed it to repo as well

{
  "version": "1.0",
  "components": [
    "Microsoft.VisualStudio.Component.CoreEditor",
    "Microsoft.VisualStudio.Workload.CoreEditor",
    "Microsoft.NetCore.Component.Runtime.3.1",
    "Microsoft.NetCore.Component.SDK",
    "Microsoft.VisualStudio.Component.NuGet",
    "Microsoft.Net.Component.4.6.1.TargetingPack",
    "Microsoft.VisualStudio.Component.Roslyn.Compiler",
    "Microsoft.VisualStudio.Component.Roslyn.LanguageServices",
    "Microsoft.VisualStudio.Component.FSharp",
    "Microsoft.NetCore.Component.DevelopmentTools",
    "Microsoft.VisualStudio.Component.FSharp.WebTemplates",
    "Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions",
    "Microsoft.VisualStudio.Component.DockerTools",
    "Microsoft.NetCore.Component.Web",
    "Microsoft.Net.Component.4.8.SDK",
    "Microsoft.Net.Component.4.7.2.TargetingPack",
    "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites",
    "Microsoft.VisualStudio.Component.TypeScript.3.8",
    "Microsoft.VisualStudio.Component.JavaScript.TypeScript",
    "Microsoft.VisualStudio.Component.JavaScript.Diagnostics",
    "Microsoft.Component.MSBuild",
    "Microsoft.VisualStudio.Component.TextTemplating",
    "Component.Microsoft.VisualStudio.RazorExtension",
    "Microsoft.VisualStudio.Component.IISExpress",
    "Microsoft.VisualStudio.Component.SQL.ADAL",
    "Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime",
    "Microsoft.VisualStudio.Component.Common.Azure.Tools",
    "Microsoft.VisualStudio.Component.SQL.CLR",
    "Microsoft.VisualStudio.Component.MSODBC.SQL",
    "Microsoft.VisualStudio.Component.MSSQL.CMDLnUtils",
    "Microsoft.VisualStudio.Component.ManagedDesktop.Core",
    "Microsoft.Net.Component.4.5.2.TargetingPack",
    "Microsoft.Net.Component.4.5.TargetingPack",
    "Microsoft.VisualStudio.Component.SQL.SSDT",
    "Microsoft.VisualStudio.Component.SQL.DataSources",
    "Component.Microsoft.Web.LibraryManager",
    "Microsoft.VisualStudio.ComponentGroup.Web",
    "Microsoft.VisualStudio.Component.Web",
    "Microsoft.VisualStudio.Component.IntelliCode",
    "Microsoft.Net.Component.4.TargetingPack",
    "Microsoft.Net.Component.4.5.1.TargetingPack",
    "Microsoft.Net.Component.4.6.TargetingPack",
    "Microsoft.Net.ComponentGroup.TargetingPacks.Common",
    "Microsoft.Net.Core.Component.SDK.2.1",
    "Component.Microsoft.VisualStudio.Web.AzureFunctions",
    "Microsoft.VisualStudio.ComponentGroup.AzureFunctions",
    "Microsoft.VisualStudio.Component.Azure.Compute.Emulator",
    "Microsoft.VisualStudio.Component.Azure.Storage.Emulator",
    "Microsoft.VisualStudio.Component.Azure.ClientLibs",
    "Microsoft.VisualStudio.Component.Azure.AuthoringTools",
    "Microsoft.VisualStudio.Component.CloudExplorer",
    "Microsoft.VisualStudio.ComponentGroup.Web.CloudTools",
    "Microsoft.VisualStudio.Component.DiagnosticTools",
    "Microsoft.VisualStudio.Component.EntityFramework",
    "Microsoft.VisualStudio.Component.AspNet45",
    "Microsoft.VisualStudio.Component.AppInsights.Tools",
    "Microsoft.VisualStudio.Component.WebDeploy",
    "Microsoft.VisualStudio.Component.Debugger.JustInTime",
    "Microsoft.Net.Component.4.6.2.TargetingPack",
    "Microsoft.Net.Component.4.7.TargetingPack",
    "Microsoft.Net.Component.4.7.1.TargetingPack",
    "Microsoft.Net.Component.4.8.TargetingPack",
    "Microsoft.VisualStudio.Workload.NetWeb",
    "Microsoft.VisualStudio.ComponentGroup.Azure.Prerequisites",
    "Microsoft.VisualStudio.Component.Azure.Waverton.BuildTools",
    "Microsoft.VisualStudio.Component.Azure.Waverton",
    "Microsoft.Component.Azure.DataLake.Tools",
    "Microsoft.VisualStudio.Component.Azure.Kubernetes.Tools",
    "Microsoft.VisualStudio.Component.Azure.ResourceManager.Tools",
    "Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools",
    "Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices",
    "Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools",
    "Microsoft.VisualStudio.Workload.Azure",
    "Microsoft.Component.PythonTools",
    "Microsoft.Component.PythonTools.Miniconda",
    "Microsoft.Component.PythonTools.Web",
    "Microsoft.VisualStudio.Component.VC.CoreIde",
    "Microsoft.VisualStudio.Component.Windows10SDK",
    "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
    "Microsoft.VisualStudio.Component.Graphics.Tools",
    "Microsoft.VisualStudio.Component.VC.DiagnosticTools",
    "Microsoft.VisualStudio.Component.Windows10SDK.18362",
    "Microsoft.ComponentGroup.PythonTools.NativeDevelopment",
    "Component.CPython3.x64",
    "Component.CPython3.x86",
    "Microsoft.VisualStudio.Workload.Python",
    "Microsoft.VisualStudio.Component.Node.Tools",
    "Microsoft.VisualStudio.Workload.Node",
    "Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites",
    "Microsoft.ComponentGroup.Blend",
    "Microsoft.VisualStudio.Component.FSharp.Desktop",
    "Component.Dotfuscator",
    "Microsoft.VisualStudio.Component.PortableLibrary",
    "Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging",
    "Microsoft.VisualStudio.Workload.ManagedDesktop",
    "Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
    "Microsoft.VisualStudio.Component.Windows10SDK.17763",
    "Microsoft.Component.NetFX.Native",
    "Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard",
    "Microsoft.VisualStudio.Component.Graphics",
    "Microsoft.VisualStudio.ComponentGroup.UWP.Xamarin",
    "Microsoft.VisualStudio.ComponentGroup.UWP.Support",
    "Microsoft.VisualStudio.Workload.Universal",
    "Component.OpenJDK",
    "Microsoft.VisualStudio.Component.MonoDebugger",
    "Microsoft.VisualStudio.Component.Merq",
    "Component.Xamarin.RemotedSimulator",
    "Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.TemplateEngine",
    "Component.Xamarin",
    "Component.Android.SDK28",
    "Component.Android.SDK25.Private",
    "Microsoft.Net.Component.3.5.DeveloperTools",
    "Microsoft.VisualStudio.Component.Unity",
    "Component.UnityEngine.x86",
    "Microsoft.VisualStudio.Workload.ManagedGame",
    "Microsoft.VisualStudio.Component.NuGet.BuildTools",
    "Microsoft.VisualStudio.Workload.Data",
    "Microsoft.VisualStudio.Workload.DataScience",
    "Microsoft.VisualStudio.Component.VSSDK",
    "Microsoft.VisualStudio.Component.DslTools",
    "Microsoft.Component.CodeAnalysis.SDK",
    "Microsoft.VisualStudio.Workload.NetCoreTools",
    "Microsoft.Component.HelpViewer",
    "Microsoft.VisualStudio.Component.Git",
    "Microsoft.VisualStudio.Component.ClassDesigner",
    "Microsoft.VisualStudio.Component.DependencyValidation.Community",
    "Component.Xamarin.Workbooks",
    "Microsoft.Net.Core.Component.SDK.2.2",
    "Microsoft.NetCore.ComponentGroup.DevelopmentTools.2.1",
    "Microsoft.NetCore.ComponentGroup.Web.2.1",
    "Microsoft.VisualStudio.Component.AzureDevOps.OfficeIntegration",
    "Microsoft.Component.ClickOnce",
    "Microsoft.Net.Component.4.6.1.SDK",
    "Microsoft.Net.Component.4.6.2.SDK",
    "Microsoft.Net.Component.4.7.SDK",
    "Microsoft.Net.Component.4.7.1.SDK",
    "Microsoft.Net.Component.4.7.2.SDK"
  ]
}

(found this from SO)

When using the .NET SDK (or any new “legacy” project as of VS 16.0) the FSharp.Core binary is a package. Things used to be absolutely nightmarish with FSharp.Core in the GAC, then not in the GAC, then at least three different ones depending on what you were doing (.NET Core 1.x was great). But now it’s just the package for anything build with VS 2019 or from .NET Core.

Since versioning could only ever be horribly complicated, here’s a full breakdown of the various versions and what they mean.

  • Package version: 4.7.1 (latest, as of writing) - no real alignment with things, just a package that increases monotonically. Happily, this one is simple.
  • Assembly version: 4.7.0.0 - aligned with F# language version. The 4 in the front used to be the CLR version 4, since F# had to be compatible across incompatible CLRs. It’s now just the major language version number.
  • Product version: 4.7.0-beta.somebuild.number+githash - The git hash corresponds to the point in time when this was built from dotnet/fsharp. The beta is needed because VS, to this day, won’t load it in process with a git hash. It’s a really weird restriction, but C# is equally affected and it hasn’t appeared to have been a problem.
  • File version: 4.700.somebuild.number - used/generated by the build and format follows whatever our distributed build system (dotnet/arcade) requires.

Currently the numbers should be:

AssemblyVersion: 4.7.0.0
FileVersion: 4.700.20.10408
ProductVersion: 4.7.0-beta.20104.8+7c4de19faf36647c1ef700e655a52350840c6f03

So the versions @voroninp reports in the deps.json in the SO question appear to line up.

The FSharp.Core binary itself is laid out under FSharp.Core.4.7.1/lib/tfm/FSharp.Core.dll and that’s been the format ever since it’s been distributed via NuGet. I doubt that will change, though we will be dropping the net45 binary in F# 5/.NET 5.

I created the side C# project and was able to load the published assembly and scan all attributes. I’ll add in-proc runner, and let you know whether it works.

Okay, silly me. I’ve solved my own problem. Turns out my project is still targeting netcoreapp2.0 and all new global cli tools are netcoreapp2.1 by default. Which makes sense that it couldn’t find my FSharp.Core.dll.

I’ve made a sample project using netcoreapp2.1, did a publish, installed dotnet-fm as a local tool in my publish folder and was able to execute the migration.