2

I have the following structure:

public class Message
{
    ...
    public MessageType MessageType { get; set; }
    public ICollection<Product> Products { get; set; }
    public ICollection<Product> ProductsWhereThisIsDefaultCompletedSale { get; set; }
}

public class Product
{
    ...
    public Guid? DefaultCompletedSaleMessageId { get; set; }
    public Message DefaultCompletedSaleMessage { get; set; }
    public ICollection<Message> Messages { get; set; }
}

public MessageType {...}

And the mapper:

EntityTypeBuilder<Product> entityBuilder;
entityBuilder
    .HasMany(p => p.Messages)
    .WithMany(m => m.Products);
entityBuilder
    .HasOne(p => p.DefaultCompletedSaleMessage)
    .WithMany(m => m.ProductsWhereThisIsDefaultCompletedSale)
    .OnDelete(Microsoft.EntityFrameworkCore.DeleteBehavior.NoAction);

And this is working nicely with the database (at least I think so...). When I add a new product to the Messages collection it's added to the ProductsMessages relationships' table. The records are loaded when I eager load them (Include(...)).

The problem is, I'm trying to get all navigation properties to auto include them on certain specific scenarios. But that's not working properly. When I run entityType.GetNavigations() (doc) on Message it doesn't return Products, it only returns the other two navigation properties, ProductsWhereThisIsDefaultCompletedSale and MessageType:

enter image description here

Any ideas on what am I doing incorrectly?

Please let me know if you need more information. I'm on .NET5. Thanks!

Progman
  • 16,827
  • 6
  • 33
  • 48
eestein
  • 4,914
  • 8
  • 54
  • 93

1 Answers1

2

It's because these are the so called skip navigations, introduced in EF Core 5.0 and used for implementing many-to-many with implicit join entity.

They are represented by ISkipNavigation interface, which for some design decision does not inherit INavigation interface returned by the GetNavigations method, so they are retrieved with separate method called GetSkipNavigations.

But both INavigation and ISkipNavigation share a common interface INavigationBase, so if its properties/methods are enough for what are you trying to do with them, you can get both type of collection navigations with something like this (needs using Microsoft.EntityFrameworkCore.Metadata;):

var allNavigations = entityType.GetNavigations()
    .Concat<INavigationBase>(entityType.GetSkipNavigations());
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • 1
    Thank you very much, Ivan. Going to test it right away. – eestein May 15 '21 at 16:25
  • Ivan, thank you, it did work. But, now I'm getting a timeout error... Maybe I'm trying to load too many properties? `"Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding."` I'm trying to eager load all relationships for specific entities. I'm using most of the code you posted here: https://stackoverflow.com/questions/49593482/entity-framework-core-2-0-1-eager-loading-on-all-nested-related-entities I thought it could also be related to circular dependency, but the the breaks during the include, not while getting the prop – eestein May 15 '21 at 16:39
  • @eestein Starting from EFC 3.0, loading many collections is highly inefficient. Try combining it with EFC 5.0 + [Split queries](https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries), e.g. `source.AsSplitQuery()` and then all these includes, and see how it goes. – Ivan Stoev May 15 '21 at 16:45
  • Worked like a charm! Thank you very much :) Are you part of the EFCore team? Just wondering because of all your answers... as a 3rd question :D would this AsSplitQuery be considered not recommended? I'm trying to get eager loading done, but I don't wanna write something that will break in the future or create future problems. Is it safe for me to use it like that? – eestein May 15 '21 at 16:59
  • 1
    I'm not participating in that project. Not even using it in my projects, just more like free time hobby :-) As of split queries, they were used internally in pre v3 versions of EFC, then in v3 they decided to switch to "single query" mode, which then caused a lot of troubles, so in v5 they brought back the pre v3 functionality, but with option. The documentation link shows pros/cons of both solutions, so you have to choose. But slow is basically "not working", so you don't seem to have a real option. Using split mode should be safe as it is officially introduced new old feature :-) – Ivan Stoev May 15 '21 at 17:10
  • Sir, you just saved my day. Thanks! – Dandry Nov 03 '21 at 07:58