This guide is for a simple single-project setup rather than a production-ready topology, however it should hopefully assist with the basics.
This guide assumes:
- You have the
.NET Core SDKinstalled (tested with version 3.1, though it may work with other versions) - You have
SQLiteinstalled and a basic knowledge of how to explore a SQLite database
We will use Paket for package management, though you can apply the same principles with NuGet.
-
Create a new folder for the project and open the directory
md FsEfTest cd FsEfTest -
Create a new F# console application project
dotnet new console -lang F# -
Add a tool manifest, then add the
Entity FrameworkandPakettoolsdotnet new tool-manifest dotnet tool install dotnet-ef dotnet tool install paket -
Convert the project to use
Paketdotnet paket convert-from-nuget
paket
dotnet paket add Microsoft.EntityFrameworkCore.Sqlite
dotnet paket add EntityFrameworkCore.FSharpdotnet CLI
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package EntityFrameworkCore.FSharpWe will use a very simple structure. Initially, a blog will simply have an ID and a URL. We will then update our model to allow each blog to have multiple blog posts.
For this example we will use record types, but "normal" classes will also work if this better suits your needs.
-
Create a file for our model e.g.
BloggingModel.fsand add it to the project (.fsproj) file -
Paste in the following content. Note that our record type is marked
CLIMutable, and that our DbSet has a backing field and is initialised with theDefaultValueattribute.module BloggingModel open System.ComponentModel.DataAnnotations open Microsoft.EntityFrameworkCore open EntityFrameworkCore.FSharp.Extensions [<CLIMutable>] type Blog = { [<Key>] Id: int Url: string } type BloggingContext() = inherit DbContext() [<DefaultValue>] val mutable blogs : DbSet<Blog> member this.Blogs with get() = this.blogs and set v = this.blogs <- v override _.OnModelCreating builder = builder.RegisterOptionTypes() // enables option values for all entities override __.OnConfiguring(options: DbContextOptionsBuilder) : unit = options.UseSqlite("Data Source=blogging.db") |> ignore
-
From the root of the project folder, run
dotnet ef migrations add Initial -
Now run migrations
dotnet ef database update -
Oh... that didn't work. No migrations were applied. Let's try again with verbose output
dotnet ef database update -v -
The
-voption is useful for diagnosing issues. We should see that theBloggingContextis found and theMigrationsfolder was created successfully, with our snapshot and migration. Now we simply need to add references to these to our.fsprojfile manually - in the correct order - so we will end up with something like this<ItemGroup> <Compile Include="BloggingModel.fs" /> <Compile Include="Migrations/BloggingContextModelSnapshot.fs" /> <Compile Include="Migrations/20200711141125_Initial.fs" /> <Compile Include="Program.fs" /> </ItemGroup>
-
Now, we can run migrations
dotnet ef database update
If we explore our SQLite database, we should now see the Blogs table has been created.
Let's update our model so that there is a relationship between blogs and posts.
-
Update the
BloggingModel.fsto have aPostsrecord type[<CLIMutable>] type Blog = { [<Key>] Id: int; Url: string } [<CLIMutable>] type Post = { [<Key>] Id: int Title: string BlogId: int Blog: Blog } type BloggingContext() = inherit DbContext() [<DefaultValue>] val mutable blogs : DbSet<Blog> member this.Blogs with get() = this.blogs and set v = this.blogs <- v [<DefaultValue>] val mutable posts : DbSet<Post> member this.Posts with get() = this.posts and set v = this.posts <- v override _.OnModelCreating builder = builder.RegisterOptionTypes() // enables option values for all entities override __.OnConfiguring(options: DbContextOptionsBuilder) : unit = options.UseSqlite("Data Source=blogging.db") |> ignore
-
Add a new migration
dotnet ef migrations add AddPostsTable -
When we look at the
AddPostsTablemigration in ourMigrationsfolder, we should see in theUpmethod that it has successfully inferred the foreign key relationship. Once we've added this file to our.fsproj, we can run migrations:dotnet ef database update
We should now be able to see the new Posts table in our database, complete with a foreign key relationship to the Blogs table.