使用ExpressionVisitor从表达式中删除条件

时间:2016-04-20 14:25:04

标签: c# linq

我正在尝试使用定义良好的模型为定义良好的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);
    }

2 个答案:

答案 0 :(得分:2)

这是关于如何修改表达式树的basic example

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

然后只需从树中删除它们或用'always true'表达式替换,而不是检查属性值。

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

答案 1 :(得分:2)

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

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

这假设你的&#34; SomeOtherObjectId ==&#34; someId&#34;&#34;表达在某些&#34;和&#34;链,所以你只需用&#34; true&#34;这会消除它的影响。

然后你做:

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

如果你的表达可能更复杂 - 这个例子应该让你知道如何处理它。