Search code examples
c#entity-framework-corelinq-to-entitieslinqkit

LinqKit applying nested filtering through extension-method not working


I'm facing a strange behavior using LinqKit with EF Core.

This is my query:

var divisionFilter = new DivisionFilter
{
    VisibilityFilter = (DivisionVisibilityFilter)true
};

await DbContext.Events.Get(new EventFilter
{
    IsVisibleFilter = (EventIsVisibleFilter)true,
    IdIncludeExcludeFilter = (EventIdIncludeExcludeFilter)new IncludeExcludeFilter<Guid> { Includes = new[] { eventId } }
}).Select(e => new
{
    Divisions = e.Divisions
        .Get(divisionFilter) // throws excepetion
        .Where(d2 => divisionFilter.FilterExpression.Expand().Invoke(d2)) // works fine
        .Select(division => new
        {
            division.DivisionId,
            division.DivisionName,
            division.DivisionGender
        })
}).SingleAsync()

Currently I use two extensions to apply the filter's FilterExpression which is of type Expression<Func<TEntity, bool>>

public static IQueryable<TEntity> Get<TEntity, TEntityFilter>(this DbSet<TEntity> dbSet, TEntityFilter filter)
    where TEntity : BaseEntity<TEntity>, new()
    where TEntityFilter : EntityFilter<TEntity>
{
    return dbSet.AsExpandable().Where(entity => filter.FilterExpression.Invoke(entity));
}

public static IEnumerable<TEntity> Get<TEntity, TEntityFilter>(this IEnumerable<TEntity> entities, TEntityFilter filter)
    where TEntity : BaseEntity<TEntity>, new()
    where TEntityFilter : EntityFilter<TEntity>
{
    return entities.Where(e => filter.FilterExpression.Expand().Invoke(e));
}

whereas the first one applies the AsExpandable to the top-table and the second one is used for filtering nested navigation-properties.

However, if using the Get on the nested property it throws the exception

The LINQ expression 'division' could not be translated

which is coming from the Select-clause.

If I directly use the Where-clause and adding Expand().Invoke(d2) it work as expected but not when using the extension. In my eyes, both calls are identical but obviously, they are not and I don't see what the issue is.


Solution

  • You have to mark your Get method as expandable:

    [Expandable(nameof(GetEnumerable))]
    public static IEnumerable<TEntity> Get<TEntity, TEntityFilter>(this IEnumerable<TEntity> entities, TEntityFilter filter)
        where TEntity : BaseEntity<TEntity>, new()
        where TEntityFilter : EntityFilter<TEntity>
    {
        return entities.Where(e => filter.FilterExpression.Expand().Invoke(e));
    }
    
    private static Expression<Func<IEnumerable<TEntity>, TEntityFilter, IEnumerable<TEntity>>> GetEnumerablet<TEntity, TEntityFilter>()
    {
        return (entities, filter) => 
            entities.Where(e => filter.FilterExpression.Expand().Invoke(e));
    }