dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.79k stars 3.19k forks source link

Improve discovery for F# explicit fields #12923

Open ctaggart opened 6 years ago

ctaggart commented 6 years ago

I'm trying to use EF Core 2.1 from F# with lazy loading proxies, but keep running into:

No field was found backing property 'hypervisor' of entity type 'Alert'. Lazy-loaded navigation properties must have backing fields. Either name the backing field so that it is picked up by convention or configure the backing field to use.

When we follow the recommended workaround to add abstract fields, they end up with an appended @ symbol.

In this case, I have:

    [<ForeignKey("HypervisorID")>]
    default val hypervisor : Hypervisor = null with get, set
    abstract member hypervisor : Hypervisor with get, set

If view it in JustDecompile to C# or similar app, the field is:

internal Hypervisor hypervisor@;

        [ForeignKey("HypervisorID")]
        public override Hypervisor hypervisor
        {
            get
            {
                return this.hypervisor@;
            }
            set
            {
                this.hypervisor@ = value;
            }
        }

This is the behavior of F# explicit fields.

All the fields I have are this way. Is there a way to map F# explicit fields so that EF 2.1 lazy loaded proxies work? This doesn't work, but this was the direction I was going:

    override this.OnModelCreating builder =
        base.OnModelCreating builder
        for entity in builder.Model.GetEntityTypes() do
            for property in entity.GetProperties() do
                sprintf "%s@" property.FieldInfo.Name |> property.SetField
ctaggart commented 6 years ago

This appears to be working for me:

    // F# explicit fields append an `@` symbol to the backing fields
    // https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/members/explicit-fields-the-val-keyword
    override this.OnModelCreating builder =
        base.OnModelCreating builder
        for entity in builder.Model.GetEntityTypes() do
            for foreignKey in entity.GetForeignKeys() do
                foreignKey.DependentToPrincipal.Name |> sprintf "%s@" |> foreignKey.DependentToPrincipal.SetField
            for property in entity.GetProperties() do
                property.Name |> sprintf "%s@" |> property.SetField
            for navigation in entity.GetNavigations() do
                navigation.Name |> sprintf "%s@" |> navigation.SetField
ajcvickers commented 6 years ago

Re-opening to consider adding this pattern to the field-matching convention.