使用通用Lambda表达式过滤IQueryable

问题描述

我正在尝试使用泛型函数过滤类DateTime?字段。但是我收到了错误"The LINQ expression Could not be translated. Either rewrite the query in a form that can be translated,or switch to client evaluation explicitly by inserting a call to either AsEnumerable(),AsAsyncEnumerable(),ToList(),or ToListAsync()."

我不知道怎么了。

这是我的扩展功能

public static IQueryable<TEntity>? FilterDateField<TEntity>(this IQueryable<TEntity> entities,Expression<Func<TEntity,DateTime?>> dbField,DateTime dateField)
{
    var t1 = entities.Where(e=>dbField.Compile().Invoke(e).HasValue && dbField.Compile().Invoke(e)== dateField);

    return t1;
}

解决方法

我从这里的另一个问题得到答案:Generic Linq to Entities filter method that accepts filter criteria and properties to be filtered

这只是在拥有FilterDateField函数的类中添加此代码

public static Expression<Func<TFirstParam,TResult>>  Compose<TFirstParam,TIntermediate,TResult>(
    this Expression<Func<TFirstParam,TIntermediate>> first,Expression<Func<TIntermediate,TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam),"param");

    var newFirst = first.Body.Replace(first.Parameters[0],param);
    var newSecond = second.Body.Replace(second.Parameters[0],newFirst);

    return Expression.Lambda<Func<TFirstParam,TResult>>(newSecond,param);
}

public static Expression Replace(this Expression expression,Expression searchEx,Expression replaceEx)
{
    return new ReplaceVisitor(searchEx,replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from,to;
    public ReplaceVisitor(Expression from,Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

他们像这样使用de Compose:

public static IQueryable<TEntity>? FilterDateField<TEntity>(this IQueryable<TEntity> entities,Expression<Func<TEntity,DateTime?>> dbField,DateTime dateField)
{
    var dateHasValue = dbField.Compose(value => value.HasValue);
    var t1 = entities.Where(dateHasValue);

    return t1;
}