2

EDIT: This is the answer Is recursive query possible in LINQ It was already answered there must have missed that one.

I'm on .NET Core 2.2.

I've looked through multiple questions asked on this site and none of the answers worked for me.

My Model looks like this (simplified):

public class Product
{
    /// <summary>
    ///     Gets or sets the Id.
    /// </summary>
    public string Id { get; set; }

    /// <summary>
    ///     Gets or sets the code.
    /// </summary>
    public string Code { get; set; }

    /// <summary>
    ///     Gets or sets the properties.
    /// </summary>
    public List<Property> Properties { get; set; }

    /// <summary>
    ///     Gets or sets the products
    /// </summary>
    public List<Product> Products { get; set; }
}

public class Property
{
    /// <summary>
    ///     Gets or sets the Id.
    /// </summary>
    public string Id { get; set; }

    /// <summary>
    ///     Gets or sets the code
    /// </summary>
    public string Code { get; set; }
}

I am trying to load a product with all it's sub products also including the properties of all the products.

First, I've been able to load all products recursively but I honestly don't know why it works.

I've tried this:

var products1 = this._context.Products.Where(x => x.Code == "TEST").Include(x => x.Products);

And it would only load the products of the first level products.

Then by pure luck/mistake I've left this line before the aforementioned one:

this._context.Products.ToList()

Then, the first line actually loaded all products recursively... nice I guess?

Anyway, now what I'm trying to do is load all products recursively while also loading all of their properties.

I've tried a couple of things including the shady-looking:

this._context.Products.Where(x => x.Code == "TEST").Include(x => x.Properties)
            .Include(x => x.Products).ThenInclude(x => x.Properties);

But whatever I do I will only load the properties of the products of the first level of recursion. I still fully load all products recursively but only the first 2 levels have their properties loaded.

So ultimately my question is how do I do that? As a bonus question that might be answered anyway if the real question is answered: why do I need to put this line:

this._context.Products.ToList()

before this line:

var products1 = this._context.Products.Where(x => x.Code == "TEST").Include(x => x.Products);

To load the products recursively?

EDIT: Note that I know I can recursively load all Product/Property with multiple queries (one for each Product) in a loop but I definitely want to avoid that if possible.

10
  • Where are you defining the relationships? Commented Apr 22, 2020 at 13:13
  • 1
    See stackoverflow.com/questions/41894751/…. Commented Apr 22, 2020 at 13:18
  • @IvanStoev oh interesting. Didn't see that one but I'll try. Commented Apr 22, 2020 at 13:39
  • 1
    @user2509192 Yes, you will be able to load other related data (with Include / ThenInclude). The "only" problem is that this is not so efficient for returning single / few items, because it needs to retrieve / process the whole table in memory (thus a lot of unnecessary related data as well). Commented Apr 22, 2020 at 14:04
  • 1
    @IvanStoev thanks a lot it works! Btw WithOptional doesn't exist anymore. We now use WithOne and when the FK is nullable it is treated as optionnal otherwise yup, your answer is THE answer. Commented Apr 22, 2020 at 14:49

1 Answer 1

1

Eager loading won't work recursively, but you can use lazy loading. It will hurt your performance, but you will be able to iterate all layers.

Refer to https://learn.microsoft.com/en-us/ef/core/querying/related-data

The simplest way to use lazy-loading is by installing the Microsoft.EntityFrameworkCore.Proxies package and enabling it with a call to UseLazyLoadingProxies. For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
    .UseLazyLoadingProxies()
    .UseSqlServer(myConnectionString);

EF Core will then enable lazy loading for any navigation property that can be overridden--that is, it must be virtual and on a class that can be inherited from.

So convert your Lists to a virtual collections and you're good to go.

Sign up to request clarification or add additional context in comments.

2 Comments

Thing is my properties aren't directly accessed within the app. The result of my query is sent through a post to another app. (as json).
@user2509192 Doesn't matter. A standard serializer like JsonConvert.SerializeObject() from Newtonsoft.Json will iterate through IEnumerable objects and include them in JSON.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.