首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用LinqKit PredicateBuilder作为相关模型(EF核心)

使用LinqKit PredicateBuilder作为相关模型(EF核心)
EN

Stack Overflow用户
提问于 2017-10-12 09:38:54
回答 2查看 10.4K关注 0票数 13

我想使用LinqKit的PredicateBuilder,并将谓词传递给相关模型的.Any方法。

所以我想构建一个谓词:

代码语言:javascript
运行
复制
var castCondition = PredicateBuilder.New<CastInfo>(true);

if (movies != null && movies.Length > 0)
{
    castCondition = castCondition.And(c => movies.Contains(c.MovieId));
}
if (roleType > 0)
{
    castCondition = castCondition.And(c => c.RoleId == roleType);
}

然后使用它过滤与谓词中的模型相关的模型:

代码语言:javascript
运行
复制
IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castCondition));
return await result.OrderBy(n => n.Name1).Take(25).ToListAsync();

但这会导致System.NotSupportedException: Could not parse expression 'n.CastInfo.Any(Convert(__castCondition_0, Func``2))': The given arguments did not match the expected arguments: Object of type 'System.Linq.Expressions.UnaryExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.

我看到了similar question,并回答了,建议使用.Compile。或者构建额外谓词的one more question

所以我试着用额外的谓词

代码语言:javascript
运行
复制
var tp = PredicateBuilder.New<Name>(true);
tp = tp.And(n => n.CastInfo.Any(castCondition.Compile()));
IQueryable<Name> result = _context.Name.AsExpandable().Where(tp);

或者直接使用编译

代码语言:javascript
运行
复制
IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castCondition.Compile()));

但是我在编译方面有一个错误:System.NotSupportedException: Could not parse expression 'n.CastInfo.Any(__Compile_0)'

那么,是否可以将PredicateBuilder的结果转换为Any

注意:我能够构建所需的行为组合表达式,但我不喜欢这一点,我需要额外的变量。

代码语言:javascript
运行
复制
System.Linq.Expressions.Expression<Func<CastInfo,bool>> castExpression = (c => true);
if (movies != null && movies.Length > 0)
{
    castExpression = (c => movies.Contains(c.MovieId));
}
if (roleType > 0)
{
    var existingExpression = castExpression;
    castExpression = c => existingExpression.Invoke(c) && c.RoleId == roleType;
}
IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castExpression.Compile()));
return await result.OrderBy(n => n.Name1).Take(25).ToListAsync();

所以我想我只是错过了一些关于建筑工人的东西。

更新版本:我使用DotNetCore2.0和LinqKit.Microsoft.EntityFrameworkCore 1.1.10

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-10-12 18:10:05

在查看代码时,人们会假设castCondition变量的类型是Expression<Func<CastInfo, bool>> (与PredicateBuilder的早期版本一样)。

但是如果是这样的话,那么n.CastInfo.Any(castCondition)甚至不应该编译(假设CastInfo是一个集合导航属性,那么编译器就会命中期望Func<CastInfo, bool>而不是Expression<Func<CastInfo, bool>>Enumerable.Any )。这是怎么回事?

在我看来,这是C#隐式操作符滥用的一个很好的例子。PredicateBuilder.New<T>方法实际上返回一个名为ExpressionStarter的类,它有许多模拟Expression的方法,但更重要的是,有隐式转换为Expression<Func<T, bool>>Func<CastInfo, bool>。后者允许将该类用于顶级Enumerable / Queryable方法,以替代相应的lambda /表达式。但是,它还防止在表达式树中使用编译时的错误,就像在您的示例中一样--编译器会发出类似于n.CastInfo.Any((Func<CastInfo, bool>)castCondition)的内容,这当然会在运行时导致异常。

LinqKit AsExpandable方法的整个思想是允许通过自定义Invoke扩展方法“调用”表达式,然后在表达式树中进行“扩展”。因此,在开始时,如果变量类型是Expression<Func<CastInfo, bool>>,那么预期的用法是:

代码语言:javascript
运行
复制
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castCondition.Invoke(c)));

但是,由于前面解释的原因,现在无法编译。因此,您必须首先将其转换为查询的外部Expression<Func<T, bool>

代码语言:javascript
运行
复制
Expression<Func<CastInfo, bool>> castPredicate = castCondition;

然后使用

代码语言:javascript
运行
复制
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castPredicate.Invoke(c)));

代码语言:javascript
运行
复制
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(castPredicate.Compile()));

为了让编译器推断表达式类型,我将创建一个自定义扩展方法,如下所示:

代码语言:javascript
运行
复制
using System;
using System.Linq.Expressions;

namespace LinqKit
{
    public static class Extensions
    {
        public static Expression<Func<T, bool>> ToExpression<T>(this ExpressionStarter<T> expr) => expr;
    }
}

然后简单地使用

代码语言:javascript
运行
复制
var castPredicate = castCondition.ToExpression();

它仍然必须在查询的之外执行,也就是说,不是工作吗?

代码语言:javascript
运行
复制
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castCondition.ToExpression().Invoke(c)));
票数 14
EN

Stack Overflow用户

发布于 2022-05-22 06:30:08

它可能与原来的问题不完全相关,但考虑到以下模式:

代码语言:javascript
运行
复制
public Class Music
{
    public int Id { get; set; }
    public List<Genre> Genres { get; set; }
}
public Class Genre
{
    public int Id { get; set; }
    public string Title { get; set; }
}

List<string> genresToFind = new() {"Pop", "Rap", "Classical"};

如果您试图在Musics列表中找到它们的类型存在的所有genresToFind,那么您可以这样做:

PredicateBuilder模型上创建Genre表达式链:

代码语言:javascript
运行
复制
var pre = PredicateBuilder.New<Genre>();
foreach (var genre in genresToFind)
{
    pre = pre.Or(g => g.Title.Contains(genre));
}

然后像这样执行您的查询:

代码语言:javascript
运行
复制
var result = await _db.Musics.AsExpandable()
    .Where(m => m.Genres
        .Any(g => pre.ToExpression().Invoke(g)))
    .ToListAsync();

ToExpression()是我们为将ExpressionStarter<Genre>类型转换为Expression<Func<Genre, bool>>而创建的通用扩展方法:

代码语言:javascript
运行
复制
public static class ExpressionExtensions
{
    public static Expression<Func<T, bool>> ToExpression<T> (this 
        ExpressionStarter<T> exp) => exp;
}

此外,您还需要用于efcore的LinqKit.Microsoft.EntityFrameworkCore包。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46706255

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档