次の式があるとします。
int setsize = 20;
Expression<Func<Foo, bool>> predicate = x => x.Seed % setsize == 1
|| x.Seed % setsize == 4;
これは基本的に、要素のセットを 20 個のパーティションに「分割」し、各セットから最初と 4 番目の要素を取得します。
この式はMongoDBに渡され、そのドライバーは MongoDB の「クエリ」に完全に変換できます。ただし、述語はオブジェクトのリスト(LINQ2Objects)などでも使用できます。この式を再利用可能(DRY)にしたいです。ただし、取得するアイテムを指定するために を渡すことができるようにしたいIEnumerable<int>
(そのため、1 と 4 は「ハードコード」されません)。
public Expression<Func<Foo, bool>> GetPredicate(IEnumerable<int> items) {
//Build expression here and return it
}
このコードを使用したLINQPadの場合:
int setsize = 20;
Expression<Func<Foo, bool>> predicate = x => x.Seed % setsize == 1 || x.Seed % setsize == 4;
predicate.Dump();
}
class Foo
{
public int Seed { get; set; }
式を調べることができます:
ここで、この式の正確な再現を構築できるようにしたいと考えていますが、渡す整数の量は可変です (つまり、1 と 4 の代わりに、[1, 5, 9, 11]
or[8]
や orを渡すことができ[1, 2, 3, 4, 5, 6, ..., 16]
ます)。
BinaryExpressionsなどを使用してみましたが、このメッセージを正しく作成できませんでした。主な問題は、述語を MongoDB に渡すときにほとんどの試みが失敗することです。「ハードコードされた」バージョンは正常に動作しますが、動的式を渡そうとするすべての試みが、C# ドライバーによって MongoDB クエリに変換されません。
{
"$or" : [{
"Seed" : { "$mod" : [20, 1] }
}, {
"Seed" : { "$mod" : [20, 4] }
}]
}
基本的に、「ハードコードされた」バージョンに対してコンパイラが生成するものを正確に複製するように、実行時に式を動的に構築したいと考えています。
どんな助けでも大歓迎です。
編集
コメントでリクエストされた(およびpastebin に投稿された)ように、以下の私の試みの1つ。ペーストビンがそれを削除するか、サービスを停止するか、または...
using MongoRepository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
class Program
{
static void Main(string[] args)
{
MongoRepository<Foo> repo = new MongoRepository<Foo>();
var reporesult = repo.All().Where(IsInSet(new[] { 1, 4 }, 20)).ToArray();
}
private static Expression<Func<Foo, bool>> IsInSet(IEnumerable<int> seeds, int setsize)
{
if (seeds == null)
throw new ArgumentNullException("s");
if (!seeds.Any())
throw new ArgumentException("No sets specified");
return seeds.Select<int, Expression<Func<Foo, bool>>>(seed => x => x.Seed % setsize == seed).JoinByOr();
}
}
public class Foo : Entity
{
public int Seed { get; set; }
}
public static class Extensions
{
public static Expression<Func<T, bool>> JoinByOr<T>(this IEnumerable<Expression<Func<T, bool>>> filters)
{
var firstFilter = filters.First();
var body = firstFilter.Body;
var param = firstFilter.Parameters.ToArray();
foreach (var nextFilter in filters.Skip(1))
{
var nextBody = Expression.Invoke(nextFilter, param);
body = Expression.Or(body, nextBody);
}
return Expression.Lambda<Func<T, bool>>(body, param);
}
}
これにより、次のようになりますUnsupported where clause: <InvocationExpression>
。