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 SDK
installed (tested with version 3.1, though it may work with other versions) - You have
SQLite
installed 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 Framework
andPaket
toolsdotnet new tool-manifest dotnet tool install dotnet-ef dotnet tool install paket
-
Convert the project to use
Paket
dotnet paket convert-from-nuget
paket
dotnet paket add Microsoft.EntityFrameworkCore.Sqlite
dotnet paket add EntityFrameworkCore.FSharp
dotnet CLI
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package EntityFrameworkCore.FSharp
We 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.fs
and 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 theDefaultValue
attribute.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
-v
option is useful for diagnosing issues. We should see that theBloggingContext
is found and theMigrations
folder was created successfully, with our snapshot and migration. Now we simply need to add references to these to our.fsproj
file 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.fs
to have aPosts
record 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
AddPostsTable
migration in ourMigrations
folder, we should see in theUp
method 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.