LINQ动态表达式,按子句顺序排列



我有以下查询:

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 + "%"));
}

所以现在aivCondExpression<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]

我还尝试从string:

构建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);

运气不好

我知道我缺少关于表达式树、编译和委托调用的知识,所以任何帮助(和解释)都将非常感谢!

EDIT/SOLUTION

所以在Richard Deeming的帮助下有了一个解决方案。他以假开始谓词是对的。我愚蠢地从一个不同的方法复制/粘贴代码,没有注意到。

作为对他的第二个评论,添加AsQueryable()允许通过一个Expression<Func<TSource, bool>>很明显但不适合我。这样翻译很好,编译器也没问题。

无论如何,还有另一种方法,使用LINQKit的AsExpandble()方法和表达式类中的内置Compile()方法,并按照LINQKit:

中的描述:

Compile是Expression类中的内置方法。它转换表达式<Purchase,bool>变成一个简单的func>Purchase, boolean>这满足了编译器。当然,如果这个方法真的运行了,我们最终得到的将是编译好的IL代码,而不是表达式树,并且LINQ to SQL或实体框架将抛出异常。但这里有一个聪明的部分:Compile永远不会真正运行;LINQ to SQL或实体框架也没有看到它。通过调用AsExpandable创建的特殊包装器完全剥离了对Compile的调用,并替换为正确的表达式树。

那么代码应该是这样的:

product = product.AsExpandable().OrderBy(d => d.ProductAttributeItem
.Where(ai => ai.AttributeItem.AttributeId == (int)DefaultAttributes.Name)
.SelectMany(p => p.AttributeItem.AttributeItemValue)
.Any(aivCond.Compile()));

谢谢大家的帮助,我希望这个问题能帮助那些在代码中迷路的人…

!

正如评论中提到的,您只需要在集合上使用AsQueryable方法来传递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);

最新更新