我有类似以下的查询
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'
所以我想动态地添加许多过滤器...在C#中我可以继续添加到我的查询中,但这不是动态的。我想在运行时这样做。如果我是从Javascript调用它,那么我也可以轻松地更新URL。
我的问题,在C#中如何动态地将这些过滤器添加到where子句中。
在普通的旧LINQ(如linq 2对象)中,我可以做这样的事情。
var machines = new string[] { "blabla1" , "blabla2" } ;
res1.Where ( x => machines.Contains(x.Name) ).ToArray()
这可行,但这对Odata Endpoint不起作用,因为我得到这样的错误。
不支持“包含”方法
所以我认为唯一的方法是动态地,编辑表达式树或其他东西来添加这些过滤器。有人知道怎么做吗?
答案 0 :(得分:4)
根据其他一些链接,我找到了几个选项。一种是动态创建表达式树,它很快变成泥潭,另一种是手动构建$ filter。但是,请使用.AddQueryOption()添加它。但是,如果查询中已经存在其他where子句,则会中断,因为生成的URL现在有两个$ filter条目。所以我所做的是获取原始查询,然后获取URL和查询字符串并获取$ filter,然后如果它存在添加我自己的动态东西并运行一个新的查询。这是一个演示(在linqpad中运行)
//Grab original query as a DataServiceQuery
DataServiceQuery<NetworkDevice> originalquery = (DataServiceQuery<NetworkDevice>)
(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();
答案 1 :(得分:1)
这是我用来将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<Func<object>>(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<T>(Expression<T> node)
{
if ((node.Body as MethodCallExpression).Method.Name == "Contains")
return TranslateContains(node);
else
return node;
}
}