1

I'm trying to make reusable includes and it currently works when I have specific concrete root entity. But let's say I have structure like this:

public class A
{
    public B NavigationB { get; set; }
}

public class B
{
    public C NavigationC { get; set; }
}

public class C
{

}

And my include extensions

public static class IncludeExtensions
{
    public static IQueryable<B> MyIncludesB(this IQueryable<B> query)
    {
        return query.Include(q => q.NavigationC);
    }

    public static IQueryable<A> MyIncludesA(this IQueryable<A> query)
    {
        return query.Include(q => q.NavigationB)
            .MyIncludesB(); // how can I implement this
    }
}

Basically everything is okay if I have single root e.g. A, but what if I want to do fetch using root B? The idea is to then include everything required for B, but when using A as root then include everything for A, and when including B, reuse MyIncludesB.

I'm not sure if this is possible because include is returning IIncludableQueryable<A, B>, but if anyone has any suggestions, feel free to help!

zhuber
  • 5,364
  • 3
  • 30
  • 63

1 Answers1

0

I think there are numerous possibilities.

My first idea would be to encapsulate the property expression:

public static class IncludeExtensions
{
    public static Expression<Func<B, C>> MyIncludeListB()
    {
        return q => q.NavigationC;
    }

    public static Expression<Func<A, B>> MyIncludeListA()
    {
        return q => q.NavigationB;
    }

    public static IQueryable<B> MyIncludesB(this IQueryable<B> query)
    {
        return query.Include(MyIncludeListB());
    }

    public static IQueryable<A> MyIncludesA(this IQueryable<A> query)
    {
        return query.Include(MyIncludeListA()).ThenInclude(MyIncludeListB());
    }
}

As you can see you can pass Expression<Func<B, C>> to the method Include

My second idea is to implement include with strings .Include(string):

public static class IncludeExtensions2
{
    public static IEnumerable<string> MyIncludeListB()
    {
        return "NavigationC".Split(";");
    }

    public static IEnumerable<string> MyIncludeListA()
    {
        return "NavigationB;NavigationB.NavigationC".Split(";");
    }

    public static IQueryable<B> MyIncludesB(this IQueryable<B> query)
    {
        foreach (var i in MyIncludeListB())
        {
            query = query.Include(i);
        }

        return query;
    }

    public static IQueryable<A> MyIncludesA(this IQueryable<A> query)
    {
        foreach (var i in MyIncludeListA())
        {
            query = query.Include(i);
        }

        return query;
    }
}

As you can see you can pass string to the method Include. You can pass the complete chain of navigation properties.

If you want to implement more than one navigation property you have to amend both code examples accordingly.

There are already some questions and answers like yours:

Easy: Entity Framework - Include Multiple Levels of Properties

Hard: Multiple Includes() in EF Core

Sebastian Siemens
  • 2,302
  • 1
  • 17
  • 24