How to include two collections of a collection using Linq - entity-framework-core

I have a DBSet, it has a collection that I want to include. that collection has two objects that I want to include. If I use .ThenInclude on the first then I can't seem to get a reference to be able to include the second.
var a = _context.ModelA.Include(x => x.CollectionB).ThenInclude(x => x.ObjectC)
Where can I put a .Include(x => x.ObjectD) // which also belongs to CollectionB

Multiple inclusion does not exist in EFCore, You have to do it like this
var a = _context.ModelA
.Include(x => x.CollectionB).ThenInclude(x => x.ObjectC)
.Include(x => x.CollectionB).ThenInclude(x => x.ObjectD)

Related

Linq Lambda expression OrderBY on a ThenInclude clause

I have a query on an object that has a collection. I need to sort on a value in the collection. How can I use an OrderBY clause on a property of the collection?
var v = await _context.CollectionA
.Include(x => x.CollectionB).ThenInclude(x => x.CollectionC)
I am hoping to sort on a property that exists on CollectionC
accomplish something like...
.OrderBy(x => x.ObjectA.CollectionB.CollectionC.Ordinal)
Try the following query:
var query = _context.CollectionA
.Include(x => x.CollectionB)
.ThenInclude(x => x.CollectionC.OrderBy(c => c.Ordinal));
Eager Loading has no direct SQL translation, but I think it is what you need.

EF.Functions.Like() for an array of string

I want to filter the IQueryable<T> with the help of EF.Functions.Like() method, which accepts a string parameter, to make use of array of strings. Also, I want this filter to be applied on an IQueryable and not on a List.
var configurations = _dbContext.Configurations
.Include(x => x.ChildTable)
.Where(x => x.Id == Id);
if (!string.IsNullOrEmpty(request.Filter))
{
configurations = configurations.Where(x => EF.Functions.Like(string.Join(", ", x.ChildTable.Select(x => x.Name).ToArray()), '%' + request.Filter + '%'));
}
In the above code, Configuration has a one-many relationship with the ChildTable. Meaning, each configuration will have a more than one ChildTable entries related. So, with that given, x.ChildTable.Select(x => x.Name) is of type IEnumerable<string>. And, I want to filter the Configuration records whose ChildTable.Name entries is like the given request.Filter.
Try the following query:
var configurations = _dbContext.Configurations
.Include(x => x.ChildTable)
.Where(x => x.Id == Id);
if (!string.IsNullOrEmpty(request.Filter))
{
configurations = configurations
.Where(x => x.ChildTable.Any(c => EF.Functions.Like(c.Name, '%' + request.Filter + '%')));
}

EF Core: Strategies for segmenting IQueryable for re-use & loading selected properties only

I'm looking for a way efficiently generate and re-use part of a (fairly large) query in Entity Framework Core.
My goals are as follows:
Generate efficient SQL; including returning only the columns
needed from the relevant child tables, and avoiding N+1 scenarios
Abstract and separate the part of the queryable that runs from the grand-child level and below (first level of ThenInclude()), because this portion of the query will be used on other parent-level entities which have common grand-child entities, and I want to keep the code DRY.
My current query looks something like this (simplified) example:
var itemDetailsQuery = getQueryable(x => x.Id == itemId);
var item = await itemDetailsQuery.FirstOrDefaultAsync();
private IQueryable<Item> getQueryable(Expression<Func<Item,bool>> predicate)
{
return _itemRepository
.GetAll()
.Where(predicate)
.Include(x => x.ItemChildren)
.ThenInclude(x => x.ItemGrandChildren)
.ThenInclude(x => x.ItemGreatGrandChildren1)
.Include(x => x.ItemChildren)
.ThenInclude(x => x.ItemGrandChildren)
.ThenInclude(x => x.ItemGreatGrandChildren2)
.Include(x => x.ItemChildren)
.ThenInclude(x => x.ItemGrandChildren)
.ThenInclude(x => x.ItemGreatGrandChildren3)
.ThenInclude(x => x.ItemGreatGreatGrandChildren)
.ThenInclude(x => x.ItemGreatGreatGreatGrandChildren);
}
The above works, but:
loads every column from the relevant rows / entities and is therefore inefficient
I have to duplicate the query code if I want to load entities of type ItemGrandChildren and below from a parent entity type other than Item.
I found the post below which was very intriguing, suggesting usage of expressions and projections to achieve the kind of result I wanted, which was very neat because I could leverage an expression as a projection in each class:
https://benjii.me/2018/01/expression-projection-magic-entity-framework-core/
My implementation of this was as follows, with each child class having its own projection, so multiple levels are chained together using this strategy (note the ToList() is necesary because my collection properties are ICollections as opposed to IEnumerables; although I am worried this may cause premature execution:
private IQueryable<Item> getQueryable(Expression<Func<Item,bool>> predicate)
{
return _itemRepository
.GetAll()
.AsExpandable()
.Where(predicate)
.Select(x => new Item
{
Id = x.Id,
Name = x.Name,
ItemChildren = x.ItemChildren.AsQueryable().Select(ItemChild.Projection).ToList(),
});
}
However, when I try this, past the second layer of children, nothing is loaded; it may be that this was not designed to be used in a chained manner with multiple layers of nesting.
I've investigated alternative strategies, including extension methods, but although I can get these to work replacing first-tier Includes, I cannot find a way to have these replace a ThenInclude so I could do something like:
private IQueryable<Item> getQueryable(Expression<Func<Item,bool>> predicate)
{
return _itemRepository
.GetAll()
.Where(predicate)
.Include(x => x.ItemChildren)
.IncludeGrandChildren();
}
private IIncludableQueryable<ItemGrandChildren, ItemGreatGrandChildren3> IncludeGrandChildren(this IQueryable<ItemGrandChildren> values)
{
return values
.Include(x => x.ItemGreatGrandChildren1)
.Include(x => x.ItemGreatGrandChildren2)
.Include(x => x.ItemGreatGrandChildren3)
.ThenInclude(x => x.ItemGreatGreatGrandChildren)
.ThenInclude(x => x.ItemGreatGreatGreatGrandChildren);
}
I get the feeling that I'm missing or misunderstanding something fundamental, but any advice would be appreciated.

Include Collection of Collection in Entity Framework

I'm using Entity Framework 4.3
I have 3 tables, Lender, Product and ProductDetail
A Lender has multiple Products and a Product has Multiple ProductDetail rows
Here's a chunk of code I'm trying to use:
Lender SingleOrDefault(Expression<Func<Lender, bool>> predicate)
{
using (var uow = new UnitOfWork(Connections.LoanComparision))
{
var r = new Repository<Lender>(uow.Context);
return r.Find(predicate)
.Where(x =>
x.IsPublished &&
x.Products.Any(y => y.IsPublished))
.Include(x => x.Products.SelectMany(y => y.ProductDetails))
.SingleOrDefault();
}
}
The issue is with the Include - I'm trying to get the Lender => Products => ProductDetails. I can't see the table in intellisense even though I know it is linked correctly. I thought the SelectMany might work but it gives me a runtime error.
Any ideas as to how this can be achieved?
You do selectmany however it is for flatten list of list to list.
To include relevant list you should do select as noted in comments noted by Developer user
Include(x => x.Products.Select(y => y.ProductDetails))

Include inner nested objects when retrieving parent from database

There are lets say ParentObject which contains ChildObjects which contain GrandChildObjects (All of these are being stored in database), basically multiple nested classes, and the thing is when i retrieve ParentObject from dbContext I want it to be fully populated/refreshed, however ChildObjects are usually null, that is fixed by including .Include(x => x.ChildObject) , but how can i do this to deeper nested objects? So far this is what I'm using to retrieve all ParentObjects:
// db -> DbContext
return db.Parents
.Include(x => x.Child1)
.Include(x => x.Child2)
.Include(x => x.Child3);
// Now ChildObjects are refreshed, however objects inside them are not (GrandChildObjects)
By simply selecting the appropriate property. Either one:
.Include(x => x.Child1.GrandChild)
Or many:
.Include(x => x.Child1.Select(c => c.GrandChild))