4

次のようなクエリがあります

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 エンドポイントに対しては機能しません。

メソッド「含む」はサポートされていません

したがって、唯一の方法は、動的に、式ツリーを編集するか、それらのフィルターを追加することだと思います。誰もそれを行う方法を知っていますか?

4

2 に答える 2

6

他のいくつかのリンクをたどると、いくつかのオプションが見つかりました。1 つは動的に泥沼になった式ツリーを作成する方法で、もう 1 つは $filter を手動で作成する方法です。ただし、 .AddQueryOption() で追加してください。ただし、クエリに他の where 句が既にある場合、結果の URL に 2 つの $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();
于 2013-04-03T18:11:28.573 に答える
0

これは、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;
        }
    }
于 2013-04-04T03:57:12.723 に答える