order by 子句中的 LINQ 动态表达式
LINQ dynamic expression in order by clause
我有以下查询:
product = product.OrderByDescending(d => d.ProductAttributeItem
.Where(ai => ai.AttributeItem.AttributeId == (int)DefaultAttributes.Name)
.SelectMany(p => p.AttributeItem.AttributeItemValue)
.Any(o => EF.Functions.Like(o.Value, "%apple%") || EF.Functions.Like(o.Value, "%samsung%"))
这实际上工作得很好。 product
是一个相当复杂的查询,它使用许多基于输入过滤器的谓词构建。这里我们对查询的 Any()
部分感兴趣,特别是谓词部分 - 如何动态生成 EF.Functions.Like(o.Value, "%apple%") || EF.Functions.Like(o.Value, "%samsung%")
。
我已经在我们的项目中使用了一些谓词生成器扩展方法,它们对非嵌套情况非常有效,例如:
var condition = PredicateBuilder.True<AttributeItemValue>();
if(filters.OnlyActivated)
condition = condition.And(product => product.IsActive);
product = _context.Product.Where(condition);
所以我尝试在循环中构建谓词:
var aivCond = PredicateBuilder.True<AttributeItemValue>();
foreach (var s in searchQueryArray)
{
aivCond = aivCond.Or(f =>
EF.Functions.Like(f.Value, "%" + s + "%"));
}
所以现在 aivCond
是 Expression<Func<AttributItemValue, bool>
类型,但这不能用于替换 Any()
中的 lambda,因为它需要 Func<TSource, bool>
。试图以这种方式编译它 aivCond.Compile()
但出现以下错误:
System.ArgumentException: Expression of type 'System.Func`2[AttributeItemValue,System.Boolean]' cannot be used for parameter of type 'System.Linq.Expressions.Expression`1[System.Func`2[AttributeItemValue,System.Boolean]]' of method 'Boolean Any[AttributeItemValue](System.Linq.IQueryable`1[AttributeItemValue]
我还尝试从字符串构建 lambda 表达式:
var filter = "f => EF.Functions.Like(f.Value, \"%apple%\") || f => EF.Functions.Like(f.Value, \"%samsung%\")";
var options = ScriptOptions.Default
.AddReferences(typeof(AttributeItemValue).Assembly)
.AddReferences(typeof(Microsoft.EntityFrameworkCore.EF).Assembly)
.AddReferences(typeof(DbFunctions).Assembly)
.AddImports("Microsoft.EntityFrameworkCore");
Func<AttributeItemValue, bool> filterExpression = await CSharpScript.EvaluateAsync<Func<AttributeItemValue, bool>>(filter, options);
运气不好。
我知道我缺少有关表达式树、编译和委托调用的知识,因此非常感谢任何帮助(和解释)!
编辑/解决方案
所以在Richard Deeming的帮助下有了解决办法。
他以 False 开始谓词是正确的。我愚蠢地 copy/pasted 从不同的方法编码而没有注意到。
关于他的第二条评论,添加 AsQueryable()
允许传递一个 Expression<Func<TSource, bool>>
,这很明显但对我来说不是。这翻译很好,编译器没问题。
无论如何,还有另一种方法,使用 LINQKit 的 AsExpandble()
方法和表达式 class 中内置的 Compile()
方法,如 LINQKit:[=31 中所述=]
Compile is an inbuilt method in the Expression class. It converts the Expression<Func<Purchase,bool> into a plain Func<Purchase,bool> which satisfies the compiler. Of course, if this method actually ran, we'd end up with compiled IL code instead of an expression tree, and LINQ to SQL or Entity Framework would throw an exception. But here's the clever part: Compile never actually runs; nor does LINQ to SQL or Entity Framework ever get to see it. The call to Compile gets stripped out entirely by a special wrapper that was created by calling AsExpandable, and substituted for a correct expression tree.
所以代码看起来像这样:
product = product.AsExpandable().OrderBy(d => d.ProductAttributeItem
.Where(ai => ai.AttributeItem.AttributeId == (int)DefaultAttributes.Name)
.SelectMany(p => p.AttributeItem.AttributeItemValue)
.Any(aivCond.Compile()));
谢谢大家的帮助,我希望这个问题能帮助那些迷失在代码中的人...!
如评论中所述,您只需在集合上使用 the AsQueryable
method 即可将 Expression<Func<TItem, bool>>
作为过滤器传入。
product = product.OrderByDescending(d => d.ProductAttributeItem
.Where(ai => ai.AttributeItem.AttributeId == (int)DefaultAttributes.Name)
.SelectMany(p => p.AttributeItem.AttributeItemValue)
.AsQueryable().Any(aivCond);
我有以下查询:
product = product.OrderByDescending(d => d.ProductAttributeItem
.Where(ai => ai.AttributeItem.AttributeId == (int)DefaultAttributes.Name)
.SelectMany(p => p.AttributeItem.AttributeItemValue)
.Any(o => EF.Functions.Like(o.Value, "%apple%") || EF.Functions.Like(o.Value, "%samsung%"))
这实际上工作得很好。 product
是一个相当复杂的查询,它使用许多基于输入过滤器的谓词构建。这里我们对查询的 Any()
部分感兴趣,特别是谓词部分 - 如何动态生成 EF.Functions.Like(o.Value, "%apple%") || EF.Functions.Like(o.Value, "%samsung%")
。
我已经在我们的项目中使用了一些谓词生成器扩展方法,它们对非嵌套情况非常有效,例如:
var condition = PredicateBuilder.True<AttributeItemValue>();
if(filters.OnlyActivated)
condition = condition.And(product => product.IsActive);
product = _context.Product.Where(condition);
所以我尝试在循环中构建谓词:
var aivCond = PredicateBuilder.True<AttributeItemValue>();
foreach (var s in searchQueryArray)
{
aivCond = aivCond.Or(f =>
EF.Functions.Like(f.Value, "%" + s + "%"));
}
所以现在 aivCond
是 Expression<Func<AttributItemValue, bool>
类型,但这不能用于替换 Any()
中的 lambda,因为它需要 Func<TSource, bool>
。试图以这种方式编译它 aivCond.Compile()
但出现以下错误:
System.ArgumentException: Expression of type 'System.Func`2[AttributeItemValue,System.Boolean]' cannot be used for parameter of type 'System.Linq.Expressions.Expression`1[System.Func`2[AttributeItemValue,System.Boolean]]' of method 'Boolean Any[AttributeItemValue](System.Linq.IQueryable`1[AttributeItemValue]
我还尝试从字符串构建 lambda 表达式:
var filter = "f => EF.Functions.Like(f.Value, \"%apple%\") || f => EF.Functions.Like(f.Value, \"%samsung%\")";
var options = ScriptOptions.Default
.AddReferences(typeof(AttributeItemValue).Assembly)
.AddReferences(typeof(Microsoft.EntityFrameworkCore.EF).Assembly)
.AddReferences(typeof(DbFunctions).Assembly)
.AddImports("Microsoft.EntityFrameworkCore");
Func<AttributeItemValue, bool> filterExpression = await CSharpScript.EvaluateAsync<Func<AttributeItemValue, bool>>(filter, options);
运气不好。
我知道我缺少有关表达式树、编译和委托调用的知识,因此非常感谢任何帮助(和解释)!
编辑/解决方案
所以在Richard Deeming的帮助下有了解决办法。 他以 False 开始谓词是正确的。我愚蠢地 copy/pasted 从不同的方法编码而没有注意到。
关于他的第二条评论,添加 AsQueryable()
允许传递一个 Expression<Func<TSource, bool>>
,这很明显但对我来说不是。这翻译很好,编译器没问题。
无论如何,还有另一种方法,使用 LINQKit 的 AsExpandble()
方法和表达式 class 中内置的 Compile()
方法,如 LINQKit:[=31 中所述=]
Compile is an inbuilt method in the Expression class. It converts the Expression<Func<Purchase,bool> into a plain Func<Purchase,bool> which satisfies the compiler. Of course, if this method actually ran, we'd end up with compiled IL code instead of an expression tree, and LINQ to SQL or Entity Framework would throw an exception. But here's the clever part: Compile never actually runs; nor does LINQ to SQL or Entity Framework ever get to see it. The call to Compile gets stripped out entirely by a special wrapper that was created by calling AsExpandable, and substituted for a correct expression tree.
所以代码看起来像这样:
product = product.AsExpandable().OrderBy(d => d.ProductAttributeItem
.Where(ai => ai.AttributeItem.AttributeId == (int)DefaultAttributes.Name)
.SelectMany(p => p.AttributeItem.AttributeItemValue)
.Any(aivCond.Compile()));
谢谢大家的帮助,我希望这个问题能帮助那些迷失在代码中的人...!
如评论中所述,您只需在集合上使用 the AsQueryable
method 即可将 Expression<Func<TItem, bool>>
作为过滤器传入。
product = product.OrderByDescending(d => d.ProductAttributeItem
.Where(ai => ai.AttributeItem.AttributeId == (int)DefaultAttributes.Name)
.SelectMany(p => p.AttributeItem.AttributeItemValue)
.AsQueryable().Any(aivCond);