F# Record Types With Entity Framework Code-First
January 6, 2015 1 Comment
I was spinning up a data layer in a new FSharp project and I thought I would take EF Code-first out for a test drive. I have use EF-CF in a couple of C# projects so I am familiar with the premise (and the promise) of code-first. The FSharp project uses record types, nested record types, and choice types exclusively so I I thought of attaching each of these types for code first in turn. The first article that I ran across was this one, which seemed like a good start. I went ahead a created a family record type like so, matching the example verbatim except I swapped out the class implementation with a record type:
1 #r "../packages/EntityFramework.6.1.2/lib/net45/EntityFramework.dll" 2 3 open System.Collections.Generic 4 open System.ComponentModel.DataAnnotations 5 open System.Data.Entity 6 7 type Family = {Id:int; LastName:string; IsRegistered:bool} 8 9 type CLFamily() = 10 inherit DbContext() 11 [<DefaultValue>] 12 val mutable m_families: DbSet<Family> 13 member public this.Families with get() = this.m_families 14 and set v = this.m_families <- v 15 16 let db = new CLFamily() 17 let family = {Id=0;LastName="New Family"; IsRegistered=true} 18 db.Families.Add(family) |> ignore 19 db.SaveChanges() |> ignore 20
But I ran into this:
So I added the Key attribute to the Record type
So I hit up stack overflow with this question and sure enough, I forgot to add a reference to that assembly. Once I added it, then it compiled. I then ran the script and I got the following error message:
1 <add name="CLFamily" 2 connectionString="Server=.;Database=FamilyDomain;Trusted_Connection=True;" 3 providerName="System.Data.SqlClient"/> 4
Ugh! It was still hitting the default connection string. I went ahead and adjusted my script to account for the connection string and I swapped out the backing values with CLIMutable:
1 #r "../packages/EntityFramework.6.1.2/lib/net45/EntityFramework.dll" 2 #r "C:/Program Files (x86)/Reference Assemblies/Microsoft/Framework/.NETFramework/v4.5.1/System.ComponentModel.DataAnnotations.dll" 3 4 open System.Collections.Generic 5 open System.ComponentModel.DataAnnotations 6 open System.Data.Entity 7 8 [<CLIMutable>] 9 type Family = {[<Key>]Id:int; LastName:string; IsRegistered:bool;} 10 11 12 type FamilyContext() = 13 inherit DbContext() 14 [<DefaultValue>] val mutable families: DbSet<Family> 15 member this.Families with get() = this.families and set f = this.families <- f 16 17 let context = new FamilyContext() 18 let connectionString = "Server=.;Database=FamilyDomain;Trusted_Connection=True;" 19 context.Database.Connection.ConnectionString <- connectionString 20 let family = {Id=0; LastName="Test"; IsRegistered=true} 21 context.Families.Add(family) |> ignore 22 context.SaveChanges() |> ignore
And sure enough, the table is created in the database and the record is persisted:
And the cool thing is that even though this is a record type, the Id does adjust to the identity value given by the database.
With that out of the way, I went to tackle nested types. I added a Child class and a list of children to the family class.
1 [<CLIMutable>] 2 type Child = {[<Key>]Id:int; FamilyId: int; FirstName:string; Gender:string; Grade:int} 3 4 [<CLIMutable>] 5 type Family = {[<Key>]Id:int; LastName:string; IsRegistered:bool; Children:Child list} 6 7 type FamilyContext() = 8 inherit DbContext() 9 [<DefaultValue>] val mutable families: DbSet<Family> 10 member this.Families with get() = this.families and set f = this.families <- f 11 [<DefaultValue>] val mutable children: DbSet<Child> 12 member this.Chidlren with get() = this.children and set c = this.children <- c 13 14 let context = new FamilyContext() 15 let connectionString = "Server=.;Database=FamilyDomain;Trusted_Connection=True;" 16 context.Database.Connection.ConnectionString <- connectionString 17 let children = [{Id=0; FamilyId=0; FirstName="Test"; Gender="Male"; Grade=5}] 18 let family = {Id=0; LastName="Test"; IsRegistered=true; Children=children } 19 context.Families.Add(family) |> ignore 20 context.SaveChanges() |> ignore
Everything compiled and ran, but the Children table was not added to the database –> though the new record was added.
Going back to stack overflow, it looks like EF Code First will not auto-update the schema unless you add some more glue code. Ugh. At that point, I might as well give up on code-first if all it brings is not having to write sql scripts…
Pingback: F# Weekly #2, 2015 | Sergey Tihon's Blog