【问题标题】:Remove a condition from an expression using an ExpressionVisitor使用 ExpressionVisitor 从表达式中删除条件
【发布时间】:2016-04-21 03:02:35
【问题描述】:

我正在尝试使用定义良好的模型为定义良好的 Web API 构建 LINQ 提供程序。我正在关注这些演练:

我已经能够创建将表达式转换为所需 URL 的查询提供程序,并且效果很好。

现在这是我无法弄清楚的下一步。想象一下,其中一个请求返回了一个这样定义的对象:

[JsonObject]
public class SomeObject
{
   [JsonProperty(PropertyName = "id")]
   public string Id {get;set;}
   [JsonProperty(PropertyName = "name")]
   public string Name {get;set;}
   [JsonProperty(PropertyName = "is_active")]
   public bool IsActive {get;set;}

   public string SomeOtherObjectId {get;set;}
}

现在你可以看到 SomeOtherObjectId 没有用 JsonProperty 属性修饰,这是因为它不是返回对象定义的一部分,但它是必需的,因为它是 get 请求所依赖的参数。这意味着像下面这样的表达式:

Expression<Func<SomeObject, bool>> exp = o => o.SomeOtherObjectId == "someId" && o.IsActive;

被翻译成类似:

blablabla/someId/....

现在 Web API 搜索受限于他们期望的参数,因此 IsActive 过滤器将被忽略,所以我想在收到响应后,我可以在 LINQ to Objects 查询中使用相同的表达式作为谓词,所以其余的过滤器被考虑在内,但为了这样做,我必须在没有 ProjectId 过滤器的情况下重新创建表达式,因为它在返回的 JSON 中不存在,被反序列化到对象中,所以它必须变成这样的东西:

Expression<Func<SomeObject, bool>> modifiedExp = o => o.IsActive;

那么我可以做类似的事情

return deserializedObject.Where(modifiedExp);

我一直在寻找如何使用 ExpressionVisitor 执行此操作的示例,但我无法理解如何在没有我想要删除的过滤器的情况下重新创建表达式。

感谢您的帮助。

更新:

感谢EvkMaKCbIMKo,因为结合他们的答案,我得到了最终的解决方案,我将其包括在内,希望它可以帮助其他人。

protected override Expression VisitBinary(BinaryExpression node)
    {
        if (node.NodeType != ExpressionType.Equal) return base.VisitBinary(node);

        if (new[] { node.Left, node.Right }
            .Select(child => child as MemberExpression)
            .Any(memberEx => memberEx != null && memberEx.Member.CustomAttributes.All(p => p.AttributeType.Name != "JsonPropertyAttribute")))
        {
            return Expression.Constant(true);
        }

        return base.VisitBinary(node);
    }

【问题讨论】:

  • 您需要知道如何在一般情况下执行此操作,或者仅针对您提供的具体表达式 (o.SomeOtherObjectId == "someId" && o.IsActive)?
  • @Evk - 在这种情况下,如果 ParameterName == "SomeOtherObjectId" 它应该从现有表达式中删除,而其余部分保持不变,无论表达式看起来如何。我不确定这是否能回答您的问题。

标签: c# linq


【解决方案1】:

这取决于你的表达式树到底有多复杂,但在最简单的情况下,你可以这样做:

class Visitor : ExpressionVisitor {
    protected override Expression VisitBinary(BinaryExpression node) {
        // SomeOtherObjectId == "someId"
        if (node.NodeType == ExpressionType.Equal) {
            foreach (var child in new[] {node.Left, node.Right}) {
                var memberEx = child as MemberExpression;
                if (memberEx != null && memberEx.Member.Name == "SomeOtherObjectId") {
                    // return True constant
                    return Expression.Constant(true);
                }
            }
        }
        return base.VisitBinary(node);
    }        
}

这假设您的“SomeOtherObjectId == "someId"" 表达式位于某个“and”链中,因此您只需将其替换为“true”即可消除它的影响。

然后你做:

var anotherExp = (Expression<Func<SomeObject, bool>>) new Visitor().Visit(exp);

如果您的表达式可能更复杂 - 这个示例应该让您了解如何处理它。

【讨论】:

  • 这正是我所寻找的,除此之外,我还缺少“return Expression.Constant(true)”。谢谢。
【解决方案2】:

这里是basic example,关于如何修改表达式树。

您需要创建自己的 ExpressionVisitor 并对其进行配置,以查找使用您要跳过的属性的条件(例如 Json 属性丢失或不同的属性)。

然后将它们从树中删除或替换为“始终为真”表达式,而不是检查属性值。

另外,here 您可以找到有关您所要求的有用信息。

【讨论】:

  • @MaKCblMKo - 不幸的是,我已经看过你提到的参考资料,但仍然缺少一些东西来获得解决方案。 +1 给了我检查 Json 属性而不是属性名称的想法,这使得解决方案更好更通用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-06
相关资源
最近更新 更多