I've created a way to set up my EF queries to take what gets Include()
via passing a parameter.
public static class AppUserExtensions
{
public static IQueryable<AppUser> FindById(this IQueryable<AppUser> source, int userId, Expression<Func<AppUser, object?>>[] includes)
{
var query = source.Where(u => u.Id == userId);
foreach (var include in includes)
query = query.Include(include);
return query;
}
}
public static class AppUserIncludes
{
public static Expression<Func<AppUser, object?>>[] Organizations => new Expression<Func<AppUser, object?>>[]
{
u => u.Campaigns,
u => u.Cantons,
u => u.States,
u => u.Countries,
};
}
The beauty of this approach is I avoid long queries strewn throughout my code and instead have a set of queries for any kind of search and then this set of arrays to select which properties to Include(). And I can use any array with any query.
But... I'm now facing the same issue for ThenInclude()
.
What I want is some kind of object to build an array of like (what I'm writing here I know is not valid):
Complex<T1, T2> Expression<Expression<Func<T1, object?>>, Expression<Func<T2, object?>>[]>
I'm struggling with exactly what/how to do this. But I want to be able to create something where I pass:
<AppUser, Campaign>(u => u.Campaigns, CampaignExtensions.Parents)
And then in the query I can do something like:
foreach (var thenInclude in thenIncludes)
query = query.Include(include).ThenInclude(thenInclude);
I know I may not be explaining this perfectly as I'm not sure how to accomplish this and that's why I'm asking. I hope this is enough that someone can help. Is there a way to do this?
Also, I assume there's no downside to the eventual query possibly having the same Include(u => u.Campaigns)
in ot 3 or 5 times. Doesn't SQL Server reduce this down to the same SQL as it figures out all the expressions?
??? - thanks - dave
Further thought. If I call Include() as follows, it returns a type that knows what type it is returning a collection of:
IIncludableQueryable<Campaign, ICollection<AppUser>> x = query.Include(campaign => campaign.Followers!);
So where T1 in this case is AppUser, it returns an object that knows Campaign needs to be the type for T2. So if there's a way to know that in the code so it is type checking as I write it, and at compile time, that would be optimal.
The information is all there so I'm guessing this is possible. I added the tag for C# because this may be more a generics question and not so much an EF question.