Skip to content

Exception with optional navigation properties #29

Open

Description

@simon-reynolds

Attempting to register optional properties automatically (#24) results in the following error in the Northwind database

System.InvalidOperationException:
The relationship from 'Products.Category' to 'Categories.Products' with foreign key properties {'CategoryId' : FSharpOption<short>} cannot target the primary key {'CategoryId' : short} because it is not compatible.
Configure a principal key or a set of compatible foreign key properties for this relationship.

Optional navigation properties need to be supported, mapping from int option to int and TEntity option to TEntity will need to be added

Activity

  1. bricelam commented on May 2, 2019

    @bricelam
    Member

    cc @AndriySvyryd in case he has any pointers

  2. AndriySvyryd commented on May 2, 2019

    @AndriySvyryd

    We would need to add explicit support for this in Core to get a nice experience.
    The best workaround I can think of is to use a Nullable private field that is wrapped by the optional property. Then configure the relationship to use the field and ignore the property.

  3. simon-reynolds commented on Mar 18, 2021

    @simon-reynolds
    CollaboratorAuthor

    Nullable private fields wouldn't be able to work on an F# record types, similar to C# records, private fields do not exist in them

    Given that C# records are now a thing, and reference types in C# can now be annotated in not nullable, is there any support for optional types similar to what is planned for record types in dotnet/efcore#11457?

  4. simon-reynolds commented on Apr 4, 2021

    @simon-reynolds
    CollaboratorAuthor

    Seems like this could be solved by dotnet/efcore#22542

  5. LiteracyFanatic commented on Jul 9, 2021

    @LiteracyFanatic
    Contributor

    Thought I would post a small working sample since it took me a while to figure out how exactly

    The best workaround I can think of is to use a Nullable private field that is wrapped by the optional property. Then configure the relationship to use the field and ignore the property.

    translates to code.

    [<AllowNullLiteral>]
    type Comment() =
        [<DefaultValue>] val mutable private _Id: int
        // Backing field is a regular class which can be null
        [<DefaultValue>] val mutable private _Parent: Comment
        [<DefaultValue>] val mutable private _Text: string
        member this.Id with get() = this._Id and set v = this._Id <- v
        // Property is converted to and from Option
        member this.Parent with get() = Option.ofObj this._Parent and set v = this._Parent <- Option.toObj v
        member this.Text with get() = this._Text and set v = this._Text <- v
    
    type CommentContext =
        inherit DbContext
    
        [<DefaultValue>] val mutable private _Comments: DbSet<Comment>
        member this.Comments with get() = this._Comments and set v = this._Comments <- v
    
        override this.OnModelCreating(modelBuilder: ModelBuilder) =
    
                member this.Configure(builder) =
                    // Tell EF Core to ignore the property and use the field directly for the relationship
                    builder.Ignore("Parent")
                        |> ignore
                    
                    builder.HasOne("_Parent")
                        .WithMany()
                        // Set the key you want to appear in the database
                        .HasForeignKey("ParentId")
                        |> ignore
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions