Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I define this relationship? #17642

Closed
TehWardy opened this issue Sep 5, 2019 · 9 comments
Closed

How do I define this relationship? #17642

TehWardy opened this issue Sep 5, 2019 · 9 comments

Comments

@TehWardy
Copy link

TehWardy commented Sep 5, 2019

I've hit a weird scenario in a many to many join.

typically you have a parent and a child like so ...

public class ParentChild
{
    [ForeignKey("From")]
    public string ParentId { get; set; }

    [ForeignKey("To")]
    public string ChildID { get; set; }

    public virtual Thing Parent { get; set; }
    public virtual Thing Child { get; set; }
}

public class Thing
{
    [Key]
    public int Id { get; set; }

    public virtual ICollection<ParentChild> Parents { get; set; }
    public virtual ICollection<ParentChild> Children { get; set; }
}

... this is a "web of data" i'm looking to define a situation where ParentChild has just one collection of "Related" where neither is the parent or the child they are simply related like this ...

public class Thing
{
    [Key]
    public int Id { get; set; }

    public virtual ICollection<Thing> RelatedThings { get; set; }
}

... is there a way to achieve this in EF Core as I can't seem to figure out how to tell EF what i mean here?

@AndriySvyryd
Copy link
Member

This is currently not possible, it requires support for Polymorphic Relationships and transparent Many-to-Many

@TehWardy
Copy link
Author

TehWardy commented Sep 6, 2019

Why has this been closed with references to unrelated functionality ???

  • This is not a polymorphism problem as my DbSet on the context is a concrete type and i'm not inheriting in to subtypes.
  • Transparent-many-to-many relationships refer to the work around hiding the join table which i'm not bothered about.

Can you explain the relationship between my problem and the functionality you're referencing?

@AndriySvyryd
Copy link
Member

@TehWardy Polymorphic relationships would allow to collapse two navigations to the same type into one:

public class ParentChild
{
    public string ParentId { get; set; }
    public string ChildId { get; set; }

    public virtual Thing Parent { get; set; }
    public virtual Thing Child { get; set; }
}

public class Thing
{
    public int Id { get; set; }

    public virtual ICollection<ParentChild> RelatedThings { get; set; }
}

The reason I mentioned transparent Many-to-Many is because you reference Thing directly in your sample:

public virtual ICollection<Thing> RelatedThings { get; set; }

@TehWardy
Copy link
Author

TehWardy commented Sep 7, 2019

Oh ... EF needs polymorphism to do the collapsing ?
cool :)

I thought the many to many thing was specificaly about hiding the join table which I don't mind having ...
Is there a way I can do the "collapse" but perhaps keep the join table in there and have EF still work even if i have to have some consessions?

This looks good enough if EF could work with it somehow ...

public class ParentChild
{
    public string ParentId { get; set; }
    public string ChildId { get; set; }

    public virtual Thing Parent { get; set; }
    public virtual Thing Child { get; set; }
}

public class Thing
{
    public int Id { get; set; }

    public virtual ICollection<ParentChild> RelatedThings { get; set; }
}

... Just wondering if there is some sort of work around to make the object model work but with correct DB structure even it requires a bit of jankyness in the code.
My thinking is that I want the db right now and i'll fix the code model as new features come in to the framework over time.

@AndriySvyryd
Copy link
Member

AndriySvyryd commented Sep 7, 2019

@TehWardy You could make the navigations private and expose a read-only one that combines them

public class Thing
{
    public int Id { get; set; }

    private ICollection<ParentChild> Parents { get; set; }
    private ICollection<ParentChild> Children { get; set; }
    public virtual IEnumerable<ParentChild> RelatedThings => Parents.Concat(Children);
}

Either way your current model and all the variants above will result in the same db structure, so it would be easy to change once the required features are supported.

@TehWardy
Copy link
Author

TehWardy commented Sep 9, 2019

sounds good ... do you know if this will work with the OData framework when expanding in to the RelatedThings collection and performing queries on it?

@AndriySvyryd
Copy link
Member

@TehWardy You wouldn't be able to use RelatedThings for server-side queries.

@TehWardy
Copy link
Author

I found a sort of "hack" that i'm testing right now where I expose one of the collections and tell the OData framework to ignore the other like this ...

public class RelatedThing
{
    public string FromId { get; set; }
    public string ToId { get; set; }

    public virtual Thing From { get; set; }
    public virtual Thing To { get; set; }
}

public class Thing
{
    public int Id { get; set; }

    public virtual ICollection<ParentChild> RelatedThings { get; set; }

    [JsonIgnore]
    public virtual ICollection<RelatedThing> ToRelatedThings { get; set; }
}

I'm having to write some code on the back end so that when a RelatedThing with From = "A" and To = "B" is added or removed it also adds the opposite From = "B" and To = "A" ... it's a bit of a hack but it gives the desired functionality and only exposes 1 collection to the API layer keeping the "oddities" in my back end code.

It's not ideal but does mean that I get full CRUD on it through EF with the extra management logic wrapped around add's / removed with the join table.

Thanks for the help on this @AndriySvyryd ... hoping to see support for this properly in 3.0

@TehWardy
Copy link
Author

ok further testing revaled that this won't work as it causes this ...
dotnet/aspnetcore#13853

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants