c# - LINQ toEntity - 使用 IQueryable.Contains 过滤数据

标签 c# entity-framework-core linq-to-entities

我已经被一个问题困扰了一段时间。 我目前正在将一个旧项目从 Entity Framework 迁移到 Entity Framework 核心 2.2.4。

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

为了方便起见,并且由于将此类请求转换为 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 Core 中,context.Database.SqlQuery 不再存在。 为了实现相同的逻辑,我必须执行以下操作:

  1. 声明一个具有单个属性的新类 DBGlobalIdentifier
public class DBGlobalIdentifier
{
    public string GLOBALID { get; set; }
}
  • 在我的上下文类中,我声明了一个使用之前定义的类 DBGlobalIdentifier 键入的 DbQuery 对象。
  • public virtual DbQuery<DBGlobalIdentifier> Identifiers { get; set; }
    
  • 最后,我愿意
  • 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;
    

    关于c# - LINQ toEntity - 使用 IQueryable.Contains 过滤数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56258955/

    相关文章:

    c# - 如何构建 LINQ to Entities 查询以直接加载子对象,而不是调用 Reference 属性或 Load()

    C# 运算符 '/' 不能应用于类型 'Vector3' 和 'float' 的操作数

    c# - 取消的任务也显示为已完成

    c# - 如何将集合中的 item.date 与日期进行比较

    asp.net-core - EF Core 间歇性错误 : The connection does not support MultipleActiveResultSets

    c# - 如何忽略ThenInclude的相关数据?

    asp.net-mvc - 如何使用 LINQ to Entities 将数据格式化为以下格式?

    c# - 将 iTextSharp 文档加载到 MemoryStream

    c# - SSL/TLS - 查找证书时出现问题

    c# - 无法从表达式中提取此函数