首页 > 解决方案 > 使用 EntityFramework Core (3.1.11) 和 SQLite 读取相关实体

问题描述

我在网上发现了类似的问题,但都没有解决我的问题,所以我决定在这里详细描述一下。这是使用 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在 db 中找到的所有记录。

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

编辑

我注意到使用CLIMutableAttribute 将类型定义更改为 F# Record 语法可以解决问题。问题是:这两种表示有什么区别。我将使用 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
    }

编辑 2

我发现这是因为 F# 类默认不允许null,并且我的实现缺少另一个接受所有参数的构造函数。因此,适用于 EF 的完整定义如下所示:

    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)

标签: .netsqlitef#entity-framework-core

解决方案


推荐阅读