Using Entity Framework Core with F#

      15 Comments on Using Entity Framework Core with F#

So this was fun… again, learned a lot… again! I blogged before about building a simple crud app that used a type provider and that worked pretty good, until I wanted to configure my continuous integration in VSTS. This didn’t work as smoothly as I wanted, because the type provider needs to connect to the database during the build. I read that I had to cache the definition or have the database setup before the build task, but still it felt uncomfortable. I thought, well why not take a look closer to home and use Entity Framework. Latest bits of course, because why not. Example code is on GitHub.

UPDATE 8/15/2018: GitHub example is updated because of some issues and there might be some other issues not fixed right now.

The types

Yes, this went pretty well. I thought I had to create mutable types to work with EF, but just adding the [<CLIMutable>] was enough. This makes the type just mutable for the compiler, not for us mortal developers. I defined a separate type for record ids because I can! Love this. Then I got all kung fu by using the and keyword, known as mutual recursive types, so I could have a Serie with a list of Episodes and have those Episodes have a navigation property to Serie. I made the latter an Option type and made sure it wasn’t mapped by using [<NotMapped>]. That way I can just create an instance of an Epsiode and later add it to a Serie.

type [<CLIMutable>] Serie =
  { Id: SerieId
    Name: string
    Description: string
    Episodes: List<Episode>
    Status: SerieStatus
  } 
and [<CLIMutable>] Episode = 
  { Id: EpisodeId
    Number: int
    Season : int
    Name: string
    Description: string
    Status: EpisodeStatus
    [<NotMapped>]Serie: Option<Serie>
  }

The context

Then the DbContext, we’ll add DbSets for the types here, this is pretty straight forward. I also tried a 2.1.0 preview 1 feature here, namely the ValueConverter. With this you can map a type before is added or retrieved from the database. I just it here to map Enum as strings to the database, instead of integer. (It might be more useful to put enums in a separate table, but that’s not the point here)

let esconvert = ValueConverter<EpisodeStatus, string>((fun v -> v.ToString()), (fun v -> Enum.Parse(typedefof<EpisodeStatus>, v) :?> EpisodeStatus))
modelBuilder.Entity<Episode>().Property(fun e -> e.Status).HasConversion(esconvert) |> ignore

The repository

Time for some abstraction over the EF bits by using a repository. I don’t if this is all functional, so suggestions are welcome.

I started with the getSerie function by trying to work with the FirstOrDefault method. Stackoverflow convinced me to be more FSharpy and use the Seq.find which flow more nicely I think. The add functionality was simple, I struggled with the update a little more because I was used to Attach entities back to context or set it Modified.

let updateSerie (context: SerieContext) (entity: Serie) = 
    let currentEntry = context.Series.Find(entity.Id)
    context.Entry(currentEntry).CurrentValues.SetValues(entity)
    context.SaveChanges |> ignore

It might look if this code will do an extra call to the database, but this is only if the entity isn’t loaded before. Otherwise the entity is retrieved from the context (memory).

I wanted to know how to do some Linq-like queries use I created a getSeriesWithAiredEpisodes function to try that out.

let getSeriesWithAiredEpisodes (context: SerieContext) = 
    query {
        for serie in context.Series do
            where (serie.Episodes.Exists (fun e -> e.Status = EpisodeStatus.Aired))
            select serie 
    }

Had some light bulb moments trying Async, this isn’t my strong point in C# either by the way.

let addSerieAsync (context: SerieContext) (entity: Serie) =
    async {
        context.Series.AddAsync(entity) |> Async.AwaitTask |> ignore
        let! _ = context.SaveChangesAsync true |> Async.AwaitTask
        return entity
    }

The configuration

Last but not least, applying the configuration. Functional applying that is! I created a CompositionRoot module so that the context is applied to the repository functions. Here you might set the connection strings too.

let configureSqlServerContext = 
    (fun () ->
        let optionsBuilder = new DbContextOptionsBuilder<SerieContext>();
        optionsBuilder.UseSqlServer(@"Server=.\SQLExpress;Database=Series;Integrated Security=SSPI;") |> ignore
        new SerieContext(optionsBuilder.Options)
    )

I used the (func () -> …) construction (don’t know how this is called) so that if you need the create a separate context, just call this function. As you can see in the code on GitHub, this sample works with SQLite and SQL Server, just change the getContext function.

So that’s it, Have fun with it! Comments, critique or suggestions = awesome & welcome!

15 thoughts on “Using Entity Framework Core with F#

  1. Brett

    I love using SQL Provider and I’m curious as to what you mean by caching a definition. How is this done?

    Reply
    1. Eelco Mulder Post author

      Well that’s the road I didn’t take; but they talk about this in thread for example: https://stackoverflow.com/questions/40685423/connect-to-sql-database-without-the-sql-type-provider

      Within the same FSharp.Data.TypeProviders project there exist DBML file, SqlData and SqlEntity type providers. The latter two require compile time connection strings or names but also support offline schema caching. So you could add the cache file to source control and then change the connection string.

      Reply
  2. Josef

    Thanks for sharing this, it is good to know I am the only one trying. The main reason for me to use EF with F# are migrarions, so I do not have to write any sql scripts manually. Keep it up!

    Reply
  3. Matt

    Hi I was wondering is there a good way to do these kimds of things with Oracle database. Does F# have a provider that works with Oracle?

    Thanks,
    Mattq

    Reply
  4. Lucas Teles

    I have some problemns with your github example,

    the function `getSerie ` in SeriesRepository dont filter in query the id, it brings all the series in database and then filter it in memory

    to work as it should, i have to change how it filters
    “`
    let getSerie (context: SerieContext) id =
    context
    .Series
    .Include(fun s -> s.Episodes)
    .FirstOrDefault (fun f -> f.Id = id)
    |> (fun x -> if box x = null then None else Some x)
    “`

    Other problem is, is not possible to navigate to Serie from Episode, this is awfull, i dont find anyway to treat it as a navigation property

    After that, i find myself using SQLProvider, SQLClient and thi wrapper for Dapper (https://github.com/AlexTroshkin/fsharp-dapper) more convenient

    tks for the contribution

    Reply
    1. Eelco Mulder Post author

      I might revisit this blog post and make some changes because of these problems (and others I found) I used some of your example code btw πŸ˜‰

      let getSerie (context: SerieContext) id =
      query {
      for serie in context.Series do
      where (serie.Id = id)
      select serie
      exactlyOne
      } |> (fun x -> if box x = null then None else Some x)

      Reply
        1. Eelco Mulder Post author

          No not yet and I think this might not be supported by EF in combination with F# right now. It looks like EF cannot deal with Option types like it deals with Nullable types in C#.

          Reply
  5. Simon

    This is a great tutorial, F# + EFCore resources are not easy to find, out there ! I just set up a very similar app using Suave for web access and a SQLite db, and I ran into a weird issue : performance are not very good, at all. A single insert takes at least 250ms, up to 450ms. This is very troubling because a raw insertion using ado.net would be more around 10ms IIRC. Have you had this kind of issues ? Do you know how to fix this ? Thanks ?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.