#.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)