说我有这个表达:
int setsize = 20;
Expression<Func<Foo, bool>> predicate = x => x.Seed % setsize == 1
|| x.Seed % setsize == 4;
这基本上将一组元素“分区”为 20 个分区,并从每个集合中检索每个第一个和第四个元素。
这个表达式被传递给MongoDB,它的驱动程序完全能够转换为 MongoDB“查询”。但是,谓词也可以用于对象列表(LINQ2Objects)等。我希望这个表达式是可重用的(DRY)。但是,我希望能够传入 anIEnumerable<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]
或[1, 2, 3, 4, 5, 6, ..., 16]
)。
我曾尝试使用BinaryExpressions等,但无法正确构造此消息。主要问题是,在将谓词传递给 MongoDB 时,我的大部分尝试都会失败。“硬编码”版本工作正常,但不知何故,我所有尝试传递动态表达式的尝试都无法通过 C# 驱动程序转换为 MongoDB 查询:
{
"$or" : [{
"Seed" : { "$mod" : [20, 1] }
}, {
"Seed" : { "$mod" : [20, 4] }
}]
}
基本上,我想在运行时动态构建表达式,使其完全复制编译器为“硬编码”版本生成的内容。
任何帮助将不胜感激。
编辑
根据评论中的要求(并发布在 pastebin 上),我的尝试之一如下。我将它发布在问题中以供参考,如果 pastebin 将其删除或停止他们的服务或...
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>
。