【问题标题】:Filtering EfCore DbSet with expression causing exception使用导致异常的表达式过滤 EfCore DbSet
【发布时间】:2021-01-31 10:51:30
【问题描述】:

尝试使用生成的表达式过滤动态数据库集时

var expression = new ExpressionBuilder.ExpressionBuilder(BaseQuery.ElementType,TreeData.Filter).Build<T>();
Logger.LogDebug("Expression builder generated expression:\n{0}", expression.ToString());

BaseQuery = BaseQuery.Where(expression);
return this;

生成的表达式

expression.ToString()
"c => c.Sources.Any(s => (s.Name == \"Intelligent Cotton Mouse\"))"

尝试执行 BaseQuery 时出现以下异常

System.InvalidOperationException: The LINQ expression 'DbSet<Source>
    .Where(s => EF.Property<Nullable<Guid>>((EntityShaperExpression: 
        EntityType: Campaign
        ValueBufferExpression: 
            (ProjectionBindingExpression: EmptyProjectionMember)
        IsNullable: False
    ), "Id") != null && EF.Property<Nullable<Guid>>((EntityShaperExpression: 
        EntityType: Campaign
        ValueBufferExpression: 
            (ProjectionBindingExpression: EmptyProjectionMember)
        IsNullable: False
    ), "Id") == EF.Property<Nullable<Guid>>(s, "CampaignId"))
    .Any(s => s.Name == "Intelligent Cotton Mouse")' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

如果我尝试手动将生成的表达式插入到 .where()

    (BaseQuery as IQueryable<Campaign>).Where(c => 
                c.Sources.Any(s => s.Name == "Intelligent Cotton Mouse"));

它有效,并且谓词成功转换为 sql。可能是什么问题?

【问题讨论】:

  • @AluanHaddad dbset 在运行时创建,我只能使用根据传递给方法的类型和条件生成的表达式
  • @AluanHaddad 我明白,但我必须使用表达式来保持方法动态
  • 我的意思是 dbset 的实体可能不同,据我了解,如果我使用您的请求,那么我将绑定到一个实体
  • @AluanHaddad 过滤的表达式已经生成了,我就是不明白为什么我手写的谓词和生成的谓词不等价

标签: c# linq entity-framework-core expression-trees ef-core-3.1


【解决方案1】:

问题显然出在动态构建的表达式中,根据我的经验,我敢打赌,问题出在某些ParameterExpression 实例中,例如c =&gt; 和@ 中使用的c 987654325@ 或s 用于s =&gt;s.Name

请注意,ToString() 是在骗你,因为即使它们在视觉上看起来一模一样,但实际上它们并不是——lambda 表达式参数是由实例绑定的,而不是名称,而且 lambda 表达式允许有未绑定的参数构造过程中的表达式,并且通常只有在尝试编译它们时才会产生错误。

这是一个为示例中的内部 Any 调用构建无效 lambda 表达式的示例(最后应该是生成相关异常的那个,因为对外部执行相同操作会给出不同的异常消息):

var obj = Expression.Parameter(typeof(Source), "s");
var body = Expression.Equal(
    Expression.Property(obj, "Name"),
    Expression.Constant("Abc"));
var param = Expression.Parameter(typeof(Source), "s");
var expr = Expression.Lambda<Func<Source, bool>>(body, param);

请注意objparam 如何具有相同的名称和类型,但实例不同。即使没有错误并且expr.ToString()给出了

s => (s.Name == "Abc")

尝试在 LINQ 查询中使用此表达式将产生运行时异常。


话虽如此,解决方案是修复表达式构建器代码以确保它使用正确的参数表达式。

例如,当从现有的 lambda 表达式组合时,它应该使用Expression.Invoke 或自定义ExpressionVisitor 将正文中使用的原始参数替换为新参数。有很多例子可以做到这一点,例如Combine two lambda expressions with inner expression

【讨论】:

  • 你是对的。问题是在 any 的主体中使用的变量是在另一个方法中创建的,当我调用 expression.Compile() 时,我可以看到关于其他变量实例的异常
猜你喜欢
  • 1970-01-01
  • 2018-02-20
  • 2020-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多