如何在C#中针对Odata源动态添加filter到LINQ查询

我有如下查询

var query = (from x in NetworkDevices where x.Name == "blabla1" || x.Name == "blabla2" select x ); 

我正在针对Odata端点运行它,因此它有效地转换为以下URL

 https://targetserver/endpoint.svc/NetworkDevices()?$filter=Name eq 'blabla1' or Name eq 'blabla2' 

所以我想动态添加许多filter…在C#我可以继续添加到我的查询,但这不是动态的。 我想在运行时这样做。 如果我是从Javascript调用它,那么我也可以轻松地更新URL。

我的问题是在C#中如何动态地将这些filter添加到where子句中。

在普通的旧LINQ(像linq 2对象)我可以做这样的事情。

 var machines = new string[] { "blabla1" , "blabla2" } ; res1.Where ( x => machines.Contains(x.Name) ).ToArray() 

这会起作用,但这对Odata端点不起作用,因为我得到这样的错误。

不支持“包含”方法

所以我认为唯一的方法是动态地,编辑表达式树或其他东西来添加这些filter。 有谁知道怎么做?

在其他一些链接之后,我找到了几个选项。 一种是动态创建表达式树,它很快变成泥潭,另一种是手动构建$ filter。 但是,请使用.AddQueryOption()添加它。 但是,如果查询中已经存在其他where子句,则会中断,因为生成的URL现在有两个$ filter条目。所以我所做的是获取原始查询,然后获取URL和查询字符串并获取$ filter,然后如果它存在添加我自己的动态东西并运行一个新的查询。 这是一个演示(在linqpad中运行)

 //Grab original query as a DataServiceQuery DataServiceQuery originalquery = (DataServiceQuery) (from x in NetworkDevices where x.Type == "switch" select x); //Get the HTTP QueryString var querystr = (originalquery).RequestUri.Query; var filter = System.Web.HttpUtility.ParseQueryString(querystr)["$filter"]; /* Create our own dynamic filter equivilant to x.Name == "x" || x.Name == "y" */ string[] names = { "device1", "device2" }; StringBuilder sb = new StringBuilder(); sb.Append("("); foreach (string s in names) { sb.Append(String.Format("Name eq '{0}'",s)); sb.Append(" or "); } sb.Remove(sb.Length - 4, 4); sb.Append(")"); var dynamicfilter = sb.ToString(); // If there was an original filter we'll add the dynamic one with AND , otherwise we'll just use the dynamicone var newfilter = dynamicfilter; if ( filter != null && filter.Trim() != string.Empty ) { newfilter = filter + " and " + newfilter; } newfilter.Dump(); var finalquery = (from x in NetworkDevices.AddQueryOption("$filter",newfilter) select x).Take(50); finalquery.Dump(); 

这是我用于将Contains转换为OrElse的ExpressionVistor的示例:

  public class WhereContainsTreeModifier : ExpressionVisitor { private Expression TranslateContains(LambdaExpression lambda) { var methodCall = lambda.Body as MethodCallExpression; var member = methodCall.Object as MemberExpression; var objectMember = Expression.Convert(member, typeof(object)); var getterLambda = Expression.Lambda>(objectMember); var getter = getterLambda.Compile(); var list = (IEnumerable)getter(); Expression result = null; foreach (object item in list) { var equal = Expression.Equal(methodCall.Arguments[0], Expression.Constant(item)); if (result == null) result = equal; else result = Expression.OrElse(result, equal); } result = Expression.Lambda(lambda.Type, result, lambda.Parameters); return result; } protected override Expression VisitLambda(Expression node) { if ((node.Body as MethodCallExpression).Method.Name == "Contains") return TranslateContains(node); else return node; } }