Как использовать базу данных Entity Framework в памяти в F #?

#.net-core #f# #entity-framework-core #in-memory-database

#.net-core #f# #entity-framework-core #база данных в памяти

Вопрос:

Я пытаюсь использовать Entity Framework Core с F # с базой данных в памяти для очень простого варианта использования:

 open System
open Microsoft.EntityFrameworkCore

type Position = {
    X: double
    Y: double
}

type Airport = {
    Id: Guid
    Name: string
    Position: Position
}

type MyContext =
    inherit DbContext

    new() = { inherit DbContext() }
    new(options: DbContextOptions<MyContext>) = { inherit DbContext(options) }

    override __.OnConfiguring optionsBuilder =
        if optionsBuilder.IsConfigured <> false then
            optionsBuilder.UseInMemoryDatabase("database_name") |> ignore

    [<DefaultValue>]
    val mutable airports: DbSet<Airport>
    member x.Airports
        with get() = x.airports
        and set value = x.airports <- value

module AirportRepository =
    let getAirport id =
        use context = new MyContext()
        query {
            for airport in context.Airports do
                where (airport.Id = id)
                select airport
                exactlyOne
        } |> (fun x -> if box x = null then None else Some x)

    let addAirport (entity: Airport) =
        use context = new MyContext()
        context.Airports.Add(entity) |> ignore
        context.SaveChanges true |> ignore

[<EntryPoint>]
let main argv =
    let airport = {
        Id = Guid.NewGuid()
        Name = "Michelle"
        Position = {
            X = 42.0
            Y = 42.0
        }
    }
    AirportRepository.addAirport airport
    0
  

но это не работает и выдает следующее исключение:

 Unhandled Exception: System.InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application se
rvice provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.Initialize(IServiceProvider scopedProvider, IDbContextOptions contextOptions, DbContext context)
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.EntryWithoutDetectChanges[TEntity](TEntity entity)
   at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState)
   at Program.AirportRepository.addAirport(Airport entity) in C:UserseperretRiderProjectsFSharpCoreFSharpCoreProgram.fs:line 43
   at Program.main(String[] argv) in C:UserseperretRiderProjectsFSharpCoreFSharpCoreProgram.fs:line 56
  

Как я могу заставить это работать, переопределение OnConfiguring присутствует, поэтому я на самом деле не то, чего мне здесь не хватает.

Ответ №1:

Мне кажется, мне интересно, является ли эта строка виновником:

 if optionsBuilder.IsConfigured <> false then
            optionsBuilder.UseInMemoryDatabase("database_name") |> ignore
  

OnConfiguring EFCore должен вызываться только один раз для каждого DbContext экземпляра, поэтому проверка здесь может не понадобиться IsConfigured . Попробуйте удалить эту if ветку и повторить попытку?

Ответ №2:

Было несколько проблем:

  • Как указал Мэтью Эбботт, OnConfiguring метод не был реализован должным образом и должен был проверить, не был ли он уже настроен (а не наоборот, как то, что я изначально сделал)
  • Кажется, мне нужно было иметь только простые типы в моем определении сущности и никаких сложных типов (потому что они будут рассматриваться как другая сущность

Решение:

 open System
open Microsoft.EntityFrameworkCore


[<CLIMutable>]
type Airport = {
    Id: Guid
    Name: string
    X: double
    Y: double
}

type MyContext =
    inherit DbContext

    new() = { inherit DbContext() }
    new(options: DbContextOptions<MyContext>) = { inherit DbContext(options) }

    override __.OnConfiguring optionsBuilder =
        if optionsBuilder.IsConfigured <> true then
            optionsBuilder.UseInMemoryDatabase("database_name") |> ignore

    [<DefaultValue>]
    val mutable airports: DbSet<Airport>
    member x.Airports
        with get() = x.airports
        and set value = x.airports <- value

module AirportRepository =
    let getAirport id =
        use context = new MyContext()
        query {
            for airport in context.Airports do
                where (airport.Id = id)
                select airport
                exactlyOne
        } |> (fun x -> if box x = null then None else Some x)

    let addAirport (entity: Airport) =
        use context = new MyContext()
        context.Airports.Add(entity) |> ignore
        context.SaveChanges true |> ignore

[<EntryPoint>]
let main argv =
    let myGuid = Guid.NewGuid()
    let airport = {
        Id = myGuid
        Name = "Michelle"
        X = 42.0
        Y = 42.0
    }
    AirportRepository.addAirport airport
    let thisAirport = AirportRepository.getAirport myGuid
    assert (thisAirport = Some airport)
    0