efcore / EFCore.FSharp

Adds F# design-time support to EF Core
MIT License
228 stars 26 forks source link

Documentation: Guide on how to create a many-to-many relationships #148

Closed EspenBrun closed 1 year ago

EspenBrun commented 1 year ago

Hello,

Something that is missing from the guide/documentation is how to create a many-to-many relationship.

Is your feature request related to a problem? Please describe. Looking at the official EF documentation for a many-to-many relationship, it looks like this:

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }

    public ICollection<Post> Posts { get; set; }
}

In F# I can't do it like that since the type must be defined before it is referenced. If I write this in F# I get the error The type 'Tag' is not defined.

[<CLIMutable>]
type Post =
    { [<Key>] PostId : int
      Title : string
      Content : string
      Tags : Tag ICollection } // Error: The type 'Tag' is not defined

[<CLIMutable>]
type Tag =
    { [<Key>] TagId : string
      Posts : Post ICollection }

Describe the solution you'd like Just a guide on best practice for defining a many-to-many relationship in EF for F#.

Describe alternatives you've considered I can see that it is possible to configure the joining relationships as well. If this would be the way to do it in F# as well, it would still be nice with an example, which also includes what the Post and Tag types look like, and how one would do the query to retrieve a Post with all its tags.

modelBuilder.Entity<Post>()
    .HasMany(p => p.Tags)
    .WithMany(p => p.Posts)
    .UsingEntity<Dictionary<string, object>>(
        "PostTag",
        j => j
            .HasOne<Tag>()
            .WithMany()
            .HasForeignKey("TagId")
            .HasConstraintName("FK_PostTag_Tags_TagId")
            .OnDelete(DeleteBehavior.Cascade),
        j => j
            .HasOne<Post>()
            .WithMany()
            .HasForeignKey("PostId")
            .HasConstraintName("FK_PostTag_Posts_PostId")
            .OnDelete(DeleteBehavior.ClientCascade));

Additional context I work in a F# project that I switched to EF to get code generation. I now have the requirement for a many-to-many relationship, which I am not sure how to do best. I am new to entitiy framework.

EspenBrun commented 1 year ago

If I can get some example code here, I can also make a PR to update the documentation

kulshekhar commented 1 year ago

Would something like this help here?

type firstType = 
     | T1 of secondType

and secondType =
     | T1 of firstType

So, taking your example,

[<CLIMutable>]
type Post =
    { [<Key>] PostId : int
      Title : string
      Content : string
      Tags : Tag ICollection } 
and [<CLIMutable>] Tag =
    { [<Key>] TagId : string
      Posts : Post ICollection }

The limitation is that both types have to be defined in the same file