c# - 使用导致异常的表达式过滤 EfCore DbSet

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

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

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"));

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

最佳答案

问题显然出在动态构建的表达式中,根据我的经验,我可以打赌它出在某些ParameterExpression 实例中,例如c 用于 c =>c.Sources,或 s 用于 s =>s.Name.

请注意 ToString() 是在骗你,因为即使它们在视觉上看起来是一样的,但实际上它们并不是 - lambda 表达式参数是由实例绑定(bind)的,而不是名称,并且lambda 表达式还允许在构造期间具有未绑定(bind)的参数表达式,并且标准情况下仅在尝试编译它们时才会生成错误。

这是一个为示例中的内部 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 .

关于c# - 使用导致异常的表达式过滤 EfCore DbSet,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65978227/

相关文章:

c# - 单元测试具有接口(interface)类型参数的方法

c# - 如何检测 UI 和游戏对象上的点击/触摸事件

c# - 包含路径表达式必须引用 type.in 预加载中定义的导航属性

stored-procedures - EF Core 2.0 如何使用 SQL 存储过程

c# - 如何在此代码中放置防护属性?

c# - 我可以将枚举传递给 Controller ​​以便模型绑定(bind)器绑定(bind)它吗?

asp.net-mvc - 如何在 Onion 架构中使用来自 Kendo UI 的 ToDataSourceResult 而不暴露 IQueryable

c# - 如何将字符串列表拆分为较小的字符串列表 block

c# - 数据上下文注册为 transient ,但内存使用量不断增长。我的 DI 配置有问题吗?

asp.net-core - 如何在生产中进行迁移