Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Core conditional (add) includes to an IQueryable

In EF6 I was used to doing this:

var orders = GetAllEntities().Include(x => x.Contact.User);
if (includeProducts)
{
    orders = orders.Include(x => x.ProductOrders.Select(y => y.RentStockOrders));
    orders = orders.Include(x => x.ProductOrders.Select(y => y.Product));
    orders = orders.Include(x => x.ProductOrders.Select(y => y.Currency));
    orders = orders.Include(x => x.ProductOrders.Select(y => y.Coupons));
    orders = orders.Include(x => x.AdditionalCosts);
    orders = orders.Include(x => x.Partner);
    orders = orders.Include(x => x.OrderCoupons.Select(y => y.Coupon.Partner));
    if (includeStock)
    {
        orders = orders.Include(x => x.ProductOrders.Select(y => y.RentStockOrders.Select(z => z.Stock)));
    }
}
if (includeInvoices)
{
    orders = orders.Include(x => x.Invoices.Select(y => y.Attachments));
}

In EF Core it is not possible to override IQueryable because it is more 'typesafe'

The first line returns a IIncludableQueryable<Order, User>, so when I would do the second Include, it wants to make it something different, for example IIncludableQueryable<Ordr,User,ProductOrder>

I mostly have a GetByIdWithCrudRelations which contains a set of bools to choose what to include and what not. sometimes it has only two, but in this case it has 8, which means it can have a lot of different outcomes if I would need to if-else everything.

Anyone got a clever solution for this?

like image 532
CularBytes Avatar asked Jan 28 '23 04:01

CularBytes


1 Answers

You can use exactly the same pattern. Just start with IQueryable<T> variable (note that IIncludableQueryable<T, P> is still IQueryable<T> with additional ThenInclude support) and use ThenInclude instead of nested Selects:

IQueryable<Order> orders = GetAllEntities().Include(x => x.Contact.User);
// or var orders = GetAllEntities().Include(x => x.Contact.User).AsQueryable();
if (includeProducts)
{
    orders = orders.Include(x => x.ProductOrders).ThenInclude(y => y.RentStockOrders);
    orders = orders.Include(x => x.ProductOrders).ThenInclude(y => y.Product);
    orders = orders.Include(x => x.ProductOrders).ThenInclude(y => y.Currency);
    orders = orders.Include(x => x.ProductOrders).ThenInclude(y => y.Coupons);
    orders = orders.Include(x => x.AdditionalCosts);
    orders = orders.Include(x => x.Partner);
    orders = orders.Include(x => x.OrderCoupons).ThenInclude(y => y.Coupon.Partner);
    if (includeStock)
    {
        orders = orders.Include(x => x.ProductOrders).ThenInclude(y => y.RentStockOrders).ThenInclude(z => z.Stock);
    }
}
if (includeInvoices)
{
    orders = orders.Include(x => x.Invoices).ThenInclude(y => y.Attachments);
}

Note that since ThenInclude chain is not nested, there is no need of different variable names x, y, z etc. - single x or similar would do the same.

Also since Include is restarting the include chain from the root, the non conditional assignments like orders = orders.Include(...) can be combined, e.g.

orders = orders
    .Include(x => x.ProductOrders).ThenInclude(y => y.RentStockOrders)
    .Include(x => x.ProductOrders).ThenInclude(y => y.Product)
    .Include(x => x.ProductOrders).ThenInclude(y => y.Currency)
    .Include(x => x.ProductOrders).ThenInclude(y => y.Coupons)
    .Include(x => x.AdditionalCosts)
    .Include(x => x.Partner)
    .Include(x => x.OrderCoupons).ThenInclude(y => y.Coupon.Partner);
like image 124
Ivan Stoev Avatar answered Feb 06 '23 12:02

Ivan Stoev