LINQ to entities - 使用 IQueryable.Contains 过滤数据

LINQ to entities - Filtering data with IQueryable.Contains

一段时间以来,我一直在为一个问题而苦苦挣扎。 我目前正在将一个旧项目从 entity framework 移动到 entity framework core 2.2.4.

在这个项目中,用户可以在向服务器请求数据之前在网页上定义很多搜索条件。 我没有详细介绍查询,但它链接了 5 个 table,其中 2 个 table 包含动态名称+值参数列表(垂直存储在 table 中) .这些参数与其父 table(实体)相关联。 查询 returns 唯一标识符列表。它们代表父 table 之一的唯一键。

为了方便起见,并且由于将此类请求转换为 LINQ 查询的复杂性,我决定首先使用 c# 字符串中的搜索条件动态构建查询。

在之前的 EntityFramework 版本中,我通过以下方式实现了这一点:

List<string> arrFilteredGlobalId = _dbContext.Database.SqlQuery<string>("select GLOBALID from.....").ToList<string>();

那我可以做:

var full = from bundle in _dbContext.BUNDLE
                where arrFilteredGlobalId.Contains(bundle.GLOBALID)
                select bundle;
int num = full.Count(); // <= works perfectly

在 EntityFramework 核心的全新版本中,context.Database.SqlQuery 不再存在。 为了实现相同的逻辑,我必须执行以下操作:

  1. 使用单个 属性
  2. 声明一个新的 class DBGlobalIdentifier
public class DBGlobalIdentifier
{
    public string GLOBALID { get; set; }
}
  1. 在我的上下文 class 中,我声明了一个 DbQuery 对象,该对象使用我之前定义的 class DBGlobalIdentifier 类型。
public virtual DbQuery<DBGlobalIdentifier> Identifiers { get; set; }
  1. 我终于做到了
IQueryable<DBGlobalIdentifier> arrFilteredGlobalId =  this._context.Identifiers.FromSql("select GLOBALID from.....");

// DBGlobalIdentifier test = arrFilteredGlobalId.First(); <== works perfectly.

var full = from bundle in _context.Bundles
                where arrFilteredGlobalId.Contains(new DBGlobalIdentifier() { GLOBALID = bundle.GLOBALID })
                select bundle;

int num = full.Count(); // <= it fails and returns an exception (see here under)

异常返回:

An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an object.
Microsoft.EntityFrameworkCore.Query.Internal.QueryOptimizer.TryOptimizeContains(ResultOperatorBase resultOperator, QueryModel queryModel)

NullReferenceException: Object reference not set to an instance of an object.
Microsoft.EntityFrameworkCore.Query.Internal.QueryOptimizer.TryOptimizeContains(ResultOperatorBase resultOperator, QueryModel queryModel)
Microsoft.EntityFrameworkCore.Query.Internal.QueryOptimizer.VisitResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, int index)
Remotion.Linq.Clauses.ResultOperatorBase.Accept(IQueryModelVisitor visitor, QueryModel queryModel, int index)
Remotion.Linq.QueryModelVisitorBase.VisitResultOperators(ObservableCollection<ResultOperatorBase> resultOperators, QueryModel queryModel)
Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.TransformingQueryModelExpressionVisitor<TVisitor>.VisitSubQuery(SubQueryExpression expression)
Remotion.Linq.Clauses.Expressions.SubQueryExpression.Accept(ExpressionVisitor visitor)
System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
Remotion.Linq.Clauses.WhereClause.TransformExpressions(Func<Expression, Expression> transformation)
Remotion.Linq.QueryModel.TransformExpressions(Func<Expression, Expression> transformation)
Microsoft.EntityFrameworkCore.Query.Internal.QueryOptimizer.Optimize(QueryCompilationContext queryCompilationContext, QueryModel queryModel)
Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.OptimizeQueryModel(QueryModel queryModel, bool asyncQuery)
Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.OptimizeQueryModel(QueryModel queryModel, bool asyncQuery)
Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateQueryExecutor<TResult>(QueryModel queryModel)
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore<TResult>(Expression query, IQueryModelGenerator queryModelGenerator, IDatabase database, IDiagnosticsLogger<Query> logger, Type contextType)
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler+<>c__DisplayClass13_0<TResult>.<Execute>b__0()
Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore<TFunc>(object cacheKey, Func<Func<QueryContext, TFunc>> compiler)
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute<TResult>(Expression query)
System.Linq.Queryable.Count<TSource>(IQueryable<TSource> source)

关于我的问题的想法?

谢谢! 弗雷德

问题的原因不是很有趣 - 异常调用堆栈指示 EF Core 查询转换器错误(仍然有很多)。很可能是由于 Contains.

new DBGlobalIdentifier 的(意外)使用引起的

查询类型的想法是正确的。由于 select 原始 SQL 查询是 可组合的 ,解决方案是使用 Select 提取值,然后使用常规 Contains

var arrFilteredGlobalId = _context.Identifiers
    .FromSql("select GLOBALID from.....")
    .Select(x => x.GLOBALID); // <--

var full = from bundle in _context.Bundles
           where arrFilteredGlobalId.Contains(bundle.GLOBALID)
           select bundle;