C# EF Core-用于IQueryable搜索的表达式树等价物

C# EF Core-用于IQueryable搜索的表达式树等价物,c#,.net-core,entity-framework-core,C#,.net Core,Entity Framework Core,我有一个初始工作流程,允许我对IQueryable中包含的对象的字符串属性执行包含式搜索: 公共静态IQueryable ApplySearch(此IQueryable可查询,字符串搜索) { //为简洁起见,省略了验证 变量表达式=可查询 .Cast() .Where(item=>item.SearchStringTree(搜索)) .表达; var result=queryable.Provider.CreateQuery(表达式); 返回结果; } 静态布尔搜索字符串树(此T值,字符串搜索

我有一个初始工作流程,允许我对
IQueryable
中包含的对象的字符串属性执行包含式搜索:

公共静态IQueryable ApplySearch(此IQueryable可查询,字符串搜索)
{
//为简洁起见,省略了验证
变量表达式=可查询
.Cast()
.Where(item=>item.SearchStringTree(搜索))
.表达;
var result=queryable.Provider.CreateQuery(表达式);
返回结果;
}
静态布尔搜索字符串树(此T值,字符串搜索)=>
value.getObjectString().Any(s=>s.Contains(search.ToLower());
静态IEnumerable GetObjectString(此T值)
{
var strings=新列表();
var properties=value.GetType()
.GetProperties()
其中(x=>x.CanRead);
foreach(属性中的var属性)
{
var t=prop.PropertyType.ToString().ToLower();
var root=t.Split('.')[0];
if(t==“system.string”)
{
Add(((string)prop.GetValue(value)).ToLower());
}
如果(!(根==“系统”))
{
strings.AddRange(prop.GetValue(value.GetObjectStrings());
}
}
返回字符串;
}
是否有可能以实体框架可以在DbContext执行之前转换的方式应用此概念

我一直在研究如何使用它来实现这一点


下面是一个展示上述
IQueryable
实现的工作。

您肯定需要为所有(嵌套的)
字符串
属性构建表达式树,基本上是多
(C#
|
)谓词表达式

类似于以下内容(代码的表达式版本):

虽然这是可行的,但它并没有多大用处,因为所有LINQ扩展方法(包括
AsEnumerable(),
ToList()`等)都使用通用接口


同样,在这两种情况下,必须事先知道查询元素的类型,例如,通用版本中的
T
,非通用版本中的
query.ElementType
。这是因为表达式树是在没有“对象”的情况下预先处理的,因此它不能使用
item.GetType()
。出于同样的原因,
IQueryable
像EF Core这样的翻译人员不喜欢在查询表达式树中使用
Cast
“调用”。

感谢您的反馈!这是否适用于实体框架核心?当我在将
IQueryable
数据加载到内存之前运行问题中的示例时,抛出了一个异常,基本上表示EF无法将逻辑转换为SQL。我希望它在从
DbContext
查询数据之前执行的主要原因是我希望它与分页和排序一起工作。我注意到的另一件事是,您将
ApplySearch
设置为通用,但我使用的是一个标准
IQueryable
,没有类型信息(因此所有的反射)。(1)是,这个方法允许EF核心查询转换(2)我注意到您使用了非泛型的
IQueryable
,但发现它有点奇怪,特别是在
Cast
之前和
Cast
之后。请注意,EF Core不支持此类
Cast
调用。大多数情况下,您会像使用标准LINQ一样键入
IQueryable
。无论如何,如果您确实希望
ApplySearch
使用非泛型
IQueryable
,修改相对简单,因为主要工作是在私有递归方法中查找字符串属性并构建表达式。之所以使用非泛型的
IQueryable
,是因为源是从
ActionFilterAttribute.OnResultExecutionAsync
方法。再次感谢你的帮助。与编写LINQ查询相比,表达式树语法有点吓人,但这肯定足以让我达到我需要的程度。
public static class FilterExpression
{
    public static IQueryable<T> ApplySearch<T>(this IQueryable<T> source, string search)
    {
        if (source == null) throw new ArgumentNullException(nameof(source));
        if (string.IsNullOrWhiteSpace(search)) return source;

        var parameter = Expression.Parameter(typeof(T), "e");
        // The following simulates closure to let EF Core create parameter rather than constant value (in case you use `Expresssion.Constant(search)`)
        var value = Expression.Property(Expression.Constant(new { search }), nameof(search));
        var body = SearchStrings(parameter, value);
        if (body == null) return source;

        var predicate = Expression.Lambda<Func<T, bool>>(body, parameter);
        return source.Where(predicate);
    }

    static Expression SearchStrings(Expression target, Expression search)
    {
        Expression result = null;

        var properties = target.Type
          .GetProperties()
          .Where(x => x.CanRead);

        foreach (var prop in properties)
        {
            Expression condition = null;
            var propValue = Expression.MakeMemberAccess(target, prop);
            if (prop.PropertyType == typeof(string))
            {
                var comparand = Expression.Call(propValue, nameof(string.ToLower), Type.EmptyTypes);
                condition = Expression.Call(comparand, nameof(string.Contains), Type.EmptyTypes, search);
            }
            else if (!prop.PropertyType.Namespace.StartsWith("System."))
            {
                condition = SearchStrings(propValue, search);
            }
            if (condition != null)
                result = result == null ? condition : Expression.OrElse(result, condition);
        }

        return result;
    }
}
public static IQueryable ApplySearch(this IQueryable source, string search)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (string.IsNullOrWhiteSpace(search)) return source;

    var parameter = Expression.Parameter(source.ElementType, "e");
    var value = Expression.Property(Expression.Constant(new { search }), nameof(search));
    var body = SearchStrings(parameter, value);
    if (body == null) return source;

    var predicate = Expression.Lambda(body, parameter);
    var filtered = Expression.Call(
        typeof(Queryable), nameof(Queryable.Where), new[] { source.ElementType },
        source.Expression, Expression.Quote(predicate));
    return source.Provider.CreateQuery(filtered);
}