Чтение связанных сущностей с помощью ядра EntityFramework (3.1.11) и SQLite

#.net #sqlite #f# #entity-framework-core

Вопрос:

Я нашел похожие вопросы, размещенные в Интернете, но ни один из них не решил мою проблему, поэтому я решил описать ее подробно здесь. Вот простое соотношение «Один ко многим», созданное с помощью EF Core:

 type Parent() =
    member val ParentId = Guid()                with get, set
    member val Name = ""                        with get, set
    member val Relatives = new List<Relative>() with get, set

and Relative() =
    member val RemoteKey = Guid() with get, set
    member val LocalField = ""    with get, set
    member val Parent = Parent()  with get, set

type TestContext(fileName: string) =
    inherit DbContext()

    [<DefaultValue>] val mutable parent : DbSet<Parent>
    [<DefaultValue>] val mutable relative : DbSet<Relative>

    member this.Parent with get() = this.parent and set p = this.parent <- p
    member this.Relative with get() = this.relative and set r = this.relative <- r

    override this.OnConfiguring(optionsBuilder: DbContextOptionsBuilder) =
        optionsBuilder.UseSqlite(dbConnection) |> ignore

    override this.OnModelCreating(builder: ModelBuilder) =
        builder.Entity<Parent>(fun entity -> 
            entity.HasKey(fun p -> p.ParentId :> obj) |> ignore
        ) |> ignore

        builder.Entity<Relative>(fun entity ->
            entity.HasKey(fun r -> (r.RemoteKey, r.LocalField) :> obj) |> ignore

            entity
                .HasOne(fun r -> r.Parent)
                .WithMany(fun p -> p.Relatives :> seq<_>)
                .HasPrincipalKey(fun p -> p.ParentId :> obj)
                .HasForeignKey(fun r -> r.RemoteKey :> obj)
            |> ignore
        ) |> ignore
 

Приведенный ниже запрос возвращает все Relative записи, найденные в базе данных.

 use db = new TestContext(fileName)
let relatives = 
    query {
            for r in db.Relative.Include(fun r -> r.Parent) do
                select r
          } |> Seq.toArray
// Assuming 'relatives' is not empty, this prints parent's name
printfn "ParentName: %s" (relatives.[0].Parent.Name)
 

Запрос явно запрашивает включение внешней записи в запрос путем вызова .Include(fun r -> r.Parent) .
Однако это не работает в другом направлении:

 use db = new TestContext(fileName)
let parents = 
    query {
            for p in db.Parent.Include(fun p -> p.Relatives) do
                select p
          } |> Seq.toArray
// Even assuming that 'parents' is not empty and has related entries in the db, this prints an empty array
printfn "Relatives: %A" (parents.[0].Relatives)
 

Вопрос в следующем: как я могу ссылаться на Relative записи, на которые есть ссылка Parent ?

Редактировать

Я заметил, что изменение определений типов на синтаксис записи F# с CLIMutable атрибутом устраняет проблему. Вопрос в том, в чем разница между этими двумя представлениями. Я собираюсь использовать ILSpy и вскоре обновлю его.

 // This works with .Include(fun p -> p.Relatives)
[<CLIMutable>]
type Parent = 
    {
        ParentId : Guid
        Name : string
        Relatives : List<Relatives>
    }
and [<CLIMutable>] Relative =
    {
        RemoteKey : Guid
        LocalField : string
        Parent : Parent
    }
 

EDIT 2

I’ve worked out this is due to the fact the F# class does not allow null by default and my implementation is missing another constructor, that accepts all the parameters. So the complete definition that works with EF looks as follows:

     type [<AllowNullLiteral>] Parent(gid: Guid, field: string, relatives: ICollection<Related>) =
        member val Id = gid with get, set
        member val Field = field with get, set
        member val Relative = relatives with get, set

        new() = Parent(Guid.NewGuid(), null, null)

    and [<AllowNullLiteral>] Relative(pid: Guid, field: string, parent: Parent) =
        member val ParentId = pid with get, set
        member val LocalField = field with get, set
        member val Parent = parent with get, set

        new() = Related(Guid.NewGuid(), null, null)