-1

In our project we have texts for multiple languages stored in our database. I want to create a helper function that includes a text in a query.

This would be useful because this include happens a lot in the application and I want have the include code in one place.

The include should use the new filtered includes from Entity Framework Core 5.

This is what I want to replace:

.Include(c => c.NameTexts.Where(t => t.LanguageId == langId))

Replace it for:

.IncludeText(e => e.NameTexts, langId)

The function I want to write:

// The helper function:
public static IQueryable<T> IncludeText<T>(this IQueryable<T> originalQuery, Expression<Func<T, IEnumerable<UserText>>> textToInclude, int langId) where T : class
{
     var textWhere = textToInclude.Where(e => e.LanguageId == langId);
     originalQuery.Include(textWhere);
     return originalQuery;
}

// And call it from a query like this:
var result = await _context.SomeEntity
                .IncludeText(e => e.NameTexts, langId)
                // Instead of
                // .Include(c => c.NameTexts.Where(t => t.LanguageId == langId))
                .Where(c => c.Id == request.Id)
                .SingleOrDefaultAsync(cancellationToken);

I tried doing the following but I get an error because the types don't match.

Expression<Func<UserText, bool>> newPred = t => t.LanguageId == langId;
var textWhere = Expression.Lambda<Func<T, IList<UserText>>>(Expression.AndAlso(textToInclude, newPred), textToInclude.Parameters);

originalQuery.Include(textWhere);
return originalQuery;
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Luuk
  • 3
  • 3
  • Are you talking about the (classic - full .NET framework) Entity Framework 5 - or are you referring to the new Entity Framework **Core v5** ?? Please be very clear in your post body and in the tags you use! – marc_s Jan 22 '21 at 15:47
  • Entity Framework **Core** v5, sorry forgot that full .NET also had a EF 5 version. – Luuk Jan 22 '21 at 15:49

1 Answers1

0

Maybe this can work :

public static IQueryable<T> IncludeText<T>(this IQueryable<T> originalQuery, Expression<Func<T, IEnumerable<UserText>>> textToInclude, int langId) where T : class
{
    var methodWhere = typeof(Enumerable)
        .GetMethods()
        .First(m => m.ToString() == "System.Collections.Generic.IEnumerable`1[TSource] Where[TSource](System.Collections.Generic.IEnumerable`1[TSource], System.Func`2[TSource,System.Boolean])")
        .MakeGenericMethod(typeof(UserText));
    Expression<Func<UserText, bool>> predicate = t => t.LanguageId == langId;
    var navigationWhere = Expression.Call(methodWhere, textToInclude.Body, predicate);
    var lambda = Expression.Lambda<Func<T, IEnumerable<UserText>>>(navigationWhere, Expression.Parameter(typeof(T)));
    return originalQuery.Include(lambda);
}

I dislike how Where MethodInfo is recuperated. You can improve by inspiring from this other question.

vernou
  • 6,818
  • 5
  • 30
  • 58
  • Thanks! this works, I replaced the methodInfo with the code from the other question. – Luuk Jan 26 '21 at 17:00